Release 4.11 mm/page_io.c
/*
* linux/mm/page_io.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*
* Swap reorganised 29.12.95,
* Asynchronous swapping added 30.12.95. Stephen Tweedie
* Removed race in async swapping. 14.4.1996. Bruno Haible
* Add swap of shared pages through the page cache. 20.2.1998. Stephen Tweedie
* Always use brw_page, life becomes simpler. 12 May 1998 Eric Biederman
*/
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/gfp.h>
#include <linux/pagemap.h>
#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 <linux/blkdev.h>
#include <linux/uio.h>
#include <asm/pgtable.h>
static struct bio *get_swap_bio(gfp_t gfp_flags,
struct page *page, bio_end_io_t end_io)
{
struct bio *bio;
bio = bio_alloc(gfp_flags, 1);
if (bio) {
bio->bi_iter.bi_sector = map_swap_page(page, &bio->bi_bdev);
bio->bi_iter.bi_sector <<= PAGE_SHIFT - 9;
bio->bi_end_io = end_io;
bio_add_page(bio, page, PAGE_SIZE, 0);
BUG_ON(bio->bi_iter.bi_size != PAGE_SIZE);
}
return bio;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 45 | 46.88% | 2 | 15.38% |
Kent Overstreet | 19 | 19.79% | 2 | 15.38% |
Linus Torvalds (pre-git) | 19 | 19.79% | 5 | 38.46% |
Hugh Dickins | 10 | 10.42% | 1 | 7.69% |
Lee Schermerhorn | 1 | 1.04% | 1 | 7.69% |
Linus Torvalds | 1 | 1.04% | 1 | 7.69% |
Al Viro | 1 | 1.04% | 1 | 7.69% |
Total | 96 | 100.00% | 13 | 100.00% |
void end_swap_bio_write(struct bio *bio)
{
struct page *page = bio->bi_io_vec[0].bv_page;
if (bio->bi_error) {
SetPageError(page);
/*
* We failed to write the page out to swap-space.
* Re-dirty the page in order to avoid it being reclaimed.
* Also print a dire warning that things will go BAD (tm)
* very quickly.
*
* Also clear PG_reclaim to avoid rotate_reclaimable_page()
*/
set_page_dirty(page);
pr_alert("Write-error on swap-device (%u:%u:%llu)\n",
imajor(bio->bi_bdev->bd_inode),
iminor(bio->bi_bdev->bd_inode),
(unsigned long long)bio->bi_iter.bi_sector);
ClearPageReclaim(page);
}
end_page_writeback(page);
bio_put(bio);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Peter Zijlstra | 43 | 46.74% | 1 | 12.50% |
Andrew Morton | 36 | 39.13% | 1 | 12.50% |
Linus Torvalds (pre-git) | 5 | 5.43% | 2 | 25.00% |
Christoph Hellwig | 3 | 3.26% | 1 | 12.50% |
Kent Overstreet | 2 | 2.17% | 1 | 12.50% |
Joe Perches | 2 | 2.17% | 1 | 12.50% |
Neil Brown | 1 | 1.09% | 1 | 12.50% |
Total | 92 | 100.00% | 8 | 100.00% |
static void swap_slot_free_notify(struct page *page)
{
struct swap_info_struct *sis;
struct gendisk *disk;
/*
* There is no guarantee that the page is in swap cache - the software
* suspend code (at least) uses end_swap_bio_read() against a non-
* swapcache page. So we must check PG_swapcache before proceeding with
* this optimization.
*/
if (unlikely(!PageSwapCache(page)))
return;
sis = page_swap_info(page);
if (!(sis->flags & SWP_BLKDEV))
return;
/*
* The swap subsystem performs lazy swap slot freeing,
* expecting that the page will be swapped out again.
* So we can avoid an unnecessary write if the page
* isn't redirtied.
* This is good for real swap storage because we can
* reduce unnecessary I/O and enhance wear-leveling
* if an SSD is used as the as swap device.
* But if in-memory swap device (eg zram) is used,
* this causes a duplicated copy between uncompressed
* data in VM-owned memory and compressed data in
* zram-owned memory. So let's free zram-owned memory
* and make the VM-owned decompressed page *dirty*,
* so the page should be swapped out somewhere again if
* we again wish to reclaim it.
*/
disk = sis->bdev->bd_disk;
if (disk->fops->swap_slot_free_notify) {
swp_entry_t entry;
unsigned long offset;
entry.val = page_private(page);
offset = swp_offset(entry);
SetPageDirty(page);
disk->fops->swap_slot_free_notify(sis->bdev,
offset);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
MinChan Kim | 103 | 91.15% | 2 | 40.00% |
Andrew Morton | 8 | 7.08% | 1 | 20.00% |
Christoph Hellwig | 1 | 0.88% | 1 | 20.00% |
Neil Brown | 1 | 0.88% | 1 | 20.00% |
Total | 113 | 100.00% | 5 | 100.00% |
static void end_swap_bio_read(struct bio *bio)
{
struct page *page = bio->bi_io_vec[0].bv_page;
if (bio->bi_error) {
SetPageError(page);
ClearPageUptodate(page);
pr_alert("Read-error on swap-device (%u:%u:%llu)\n",
imajor(bio->bi_bdev->bd_inode),
iminor(bio->bi_bdev->bd_inode),
(unsigned long long)bio->bi_iter.bi_sector);
goto out;
}
SetPageUptodate(page);
swap_slot_free_notify(page);
out:
unlock_page(page);
bio_put(bio);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
MinChan Kim | 90 | 88.24% | 2 | 40.00% |
Andrew Morton | 6 | 5.88% | 1 | 20.00% |
Linus Torvalds (pre-git) | 6 | 5.88% | 2 | 40.00% |
Total | 102 | 100.00% | 5 | 100.00% |
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;
cond_resched();
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:
pr_err("swapon: swapfile has holes\n");
ret = -EINVAL;
goto out;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mel Gorman | 355 | 98.89% | 1 | 33.33% |
Mikulas Patocka | 3 | 0.84% | 1 | 33.33% |
Joe Perches | 1 | 0.28% | 1 | 33.33% |
Total | 359 | 100.00% | 3 | 100.00% |
/*
* We may have stale swap cache pages in memory: notice
* them here and get rid of the unnecessary final write.
*/
int swap_writepage(struct page *page, struct writeback_control *wbc)
{
int ret = 0;
if (try_to_free_swap(page)) {
unlock_page(page);
goto out;
}
if (frontswap_store(page) == 0) {
set_page_writeback(page);
unlock_page(page);
end_page_writeback(page);
goto out;
}
ret = __swap_writepage(page, wbc, end_swap_bio_write);
out:
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dan Magenheimer | 28 | 34.15% | 1 | 9.09% |
Seth Jennings | 17 | 20.73% | 2 | 18.18% |
Andrew Morton | 17 | 20.73% | 2 | 18.18% |
Linus Torvalds (pre-git) | 17 | 20.73% | 3 | 27.27% |
Hugh Dickins | 1 | 1.22% | 1 | 9.09% |
Mel Gorman | 1 | 1.22% | 1 | 9.09% |
Konrad Rzeszutek Wilk | 1 | 1.22% | 1 | 9.09% |
Total | 82 | 100.00% | 11 | 100.00% |
static sector_t swap_page_sector(struct page *page)
{
return (sector_t)__page_file_index(page) << (PAGE_SHIFT - 9);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew Wilcox | 25 | 96.15% | 1 | 50.00% |
Kirill A. Shutemov | 1 | 3.85% | 1 | 50.00% |
Total | 26 | 100.00% | 2 | 100.00% |
int __swap_writepage(struct page *page, struct writeback_control *wbc,
bio_end_io_t end_write_func)
{
struct bio *bio;
int ret;
struct swap_info_struct *sis = page_swap_info(page);
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
if (sis->flags & SWP_FILE) {
struct kiocb kiocb;
struct file *swap_file = sis->swap_file;
struct address_space *mapping = swap_file->f_mapping;
struct bio_vec bv = {
.bv_page = page,
.bv_len = PAGE_SIZE,
.bv_offset = 0
};
struct iov_iter from;
iov_iter_bvec(&from, ITER_BVEC | WRITE, &bv, 1, PAGE_SIZE);
init_sync_kiocb(&kiocb, swap_file);
kiocb.ki_pos = page_file_offset(page);
set_page_writeback(page);
unlock_page(page);
ret = mapping->a_ops->direct_IO(&kiocb, &from);
if (ret == PAGE_SIZE) {
count_vm_event(PSWPOUT);
ret = 0;
} else {
/*
* In the case of swap-over-nfs, this can be a
* temporary failure if the system has limited
* memory for allocating transmit buffers.
* Mark the page dirty and avoid
* rotate_reclaimable_page but rate-limit the
* messages but do not flag PageError like
* the normal direct-to-bio case as it could
* be temporary.
*/
set_page_dirty(page);
ClearPageReclaim(page);
pr_err_ratelimited("Write error on dio swapfile (%llu)\n",
page_file_offset(page));
}
end_page_writeback(page);
return ret;
}
ret = bdev_write_page(sis->bdev, swap_page_sector(page), page, wbc);
if (!ret) {
count_vm_event(PSWPOUT);
return 0;
}
ret = 0;
bio = get_swap_bio(GFP_NOIO, page, end_write_func);
if (bio == NULL) {
set_page_dirty(page);
unlock_page(page);
ret = -ENOMEM;
goto out;
}
bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
count_vm_event(PSWPOUT);
set_page_writeback(page);
unlock_page(page);
submit_bio(bio);
out:
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mel Gorman | 124 | 38.51% | 3 | 10.71% |
Andrew Morton | 54 | 16.77% | 8 | 28.57% |
Matthew Wilcox | 37 | 11.49% | 1 | 3.57% |
Seth Jennings | 35 | 10.87% | 2 | 7.14% |
Al Viro | 32 | 9.94% | 4 | 14.29% |
Linus Torvalds (pre-git) | 10 | 3.11% | 2 | 7.14% |
Jens Axboe | 9 | 2.80% | 2 | 7.14% |
Jerome Marchand | 8 | 2.48% | 1 | 3.57% |
Santosh Shilimkar | 8 | 2.48% | 1 | 3.57% |
Christoph Lameter | 2 | 0.62% | 1 | 3.57% |
Joe Perches | 1 | 0.31% | 1 | 3.57% |
Michael Christie | 1 | 0.31% | 1 | 3.57% |
Christoph Hellwig | 1 | 0.31% | 1 | 3.57% |
Total | 322 | 100.00% | 28 | 100.00% |
int swap_readpage(struct page *page)
{
struct bio *bio;
int ret = 0;
struct swap_info_struct *sis = page_swap_info(page);
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
VM_BUG_ON_PAGE(!PageLocked(page), page);
VM_BUG_ON_PAGE(PageUptodate(page), page);
if (frontswap_load(page) == 0) {
SetPageUptodate(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;
}
ret = bdev_read_page(sis->bdev, swap_page_sector(page), page);
if (!ret) {
if (trylock_page(page)) {
swap_slot_free_notify(page);
unlock_page(page);
}
count_vm_event(PSWPIN);
return 0;
}
ret = 0;
bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
if (bio == NULL) {
unlock_page(page);
ret = -ENOMEM;
goto out;
}
bio_set_op_attrs(bio, REQ_OP_READ, 0);
count_vm_event(PSWPIN);
submit_bio(bio);
out:
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 77 | 30.80% | 6 | 30.00% |
Mel Gorman | 64 | 25.60% | 1 | 5.00% |
Matthew Wilcox | 35 | 14.00% | 1 | 5.00% |
Dan Magenheimer | 23 | 9.20% | 1 | 5.00% |
MinChan Kim | 19 | 7.60% | 2 | 10.00% |
Michael Christie | 9 | 3.60% | 2 | 10.00% |
Santosh Shilimkar | 8 | 3.20% | 1 | 5.00% |
Sasha Levin | 6 | 2.40% | 1 | 5.00% |
Linus Torvalds (pre-git) | 3 | 1.20% | 2 | 10.00% |
Nicholas Piggin | 3 | 1.20% | 1 | 5.00% |
Christoph Lameter | 2 | 0.80% | 1 | 5.00% |
Konrad Rzeszutek Wilk | 1 | 0.40% | 1 | 5.00% |
Total | 250 | 100.00% | 20 | 100.00% |
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;
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
return mapping->a_ops->set_page_dirty(page);
} else {
return __set_page_dirty_no_writeback(page);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mel Gorman | 60 | 84.51% | 1 | 33.33% |
Santosh Shilimkar | 8 | 11.27% | 1 | 33.33% |
Andrew Morton | 3 | 4.23% | 1 | 33.33% |
Total | 71 | 100.00% | 3 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mel Gorman | 607 | 39.06% | 4 | 6.35% |
Andrew Morton | 253 | 16.28% | 11 | 17.46% |
MinChan Kim | 214 | 13.77% | 3 | 4.76% |
Matthew Wilcox | 97 | 6.24% | 1 | 1.59% |
Linus Torvalds (pre-git) | 75 | 4.83% | 8 | 12.70% |
Dan Magenheimer | 54 | 3.47% | 1 | 1.59% |
Seth Jennings | 52 | 3.35% | 2 | 3.17% |
Peter Zijlstra | 43 | 2.77% | 1 | 1.59% |
Al Viro | 33 | 2.12% | 5 | 7.94% |
Santosh Shilimkar | 24 | 1.54% | 1 | 1.59% |
Kent Overstreet | 23 | 1.48% | 3 | 4.76% |
Christoph Hellwig | 11 | 0.71% | 5 | 7.94% |
Hugh Dickins | 11 | 0.71% | 2 | 3.17% |
Michael Christie | 10 | 0.64% | 2 | 3.17% |
Jens Axboe | 9 | 0.58% | 2 | 3.17% |
Jerome Marchand | 8 | 0.51% | 1 | 1.59% |
Sasha Levin | 6 | 0.39% | 1 | 1.59% |
Christoph Lameter | 4 | 0.26% | 1 | 1.59% |
Joe Perches | 4 | 0.26% | 1 | 1.59% |
Mikulas Patocka | 3 | 0.19% | 1 | 1.59% |
Tejun Heo | 3 | 0.19% | 1 | 1.59% |
Nicholas Piggin | 3 | 0.19% | 1 | 1.59% |
Neil Brown | 2 | 0.13% | 1 | 1.59% |
Konrad Rzeszutek Wilk | 2 | 0.13% | 1 | 1.59% |
Kirill A. Shutemov | 1 | 0.06% | 1 | 1.59% |
Linus Torvalds | 1 | 0.06% | 1 | 1.59% |
Lee Schermerhorn | 1 | 0.06% | 1 | 1.59% |
Total | 1554 | 100.00% | 63 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.