aboutsummaryrefslogtreecommitdiffstats
path: root/fs/buffer.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-11-04 13:08:11 -0400
committerJens Axboe <axboe@fb.com>2016-11-04 16:34:47 -0400
commit29f3ad7d8380364c86556eedf4eedd3b3d4921dc (patch)
tree927d67ed0be2b321cdf6787e7e11137237419be5 /fs/buffer.c
parent600271d9000027c013c01be87cbb90a5a18c5c3f (diff)
fs: Provide function to unmap metadata for a range of blocks
Provide function equivalent to unmap_underlying_metadata() for a range of blocks. We somewhat optimize the function to use pagevec lookups instead of looking up buffer heads one by one and use page lock to pin buffer heads instead of mapping's private_lock to improve scalability. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'fs/buffer.c')
-rw-r--r--fs/buffer.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index af5776da814a..f8beca55240a 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -43,6 +43,7 @@
43#include <linux/bitops.h> 43#include <linux/bitops.h>
44#include <linux/mpage.h> 44#include <linux/mpage.h>
45#include <linux/bit_spinlock.h> 45#include <linux/bit_spinlock.h>
46#include <linux/pagevec.h>
46#include <trace/events/block.h> 47#include <trace/events/block.h>
47 48
48static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); 49static int fsync_buffers_list(spinlock_t *lock, struct list_head *list);
@@ -1636,6 +1637,81 @@ void unmap_underlying_metadata(struct block_device *bdev, sector_t block)
1636} 1637}
1637EXPORT_SYMBOL(unmap_underlying_metadata); 1638EXPORT_SYMBOL(unmap_underlying_metadata);
1638 1639
1640/**
1641 * clean_bdev_aliases: clean a range of buffers in block device
1642 * @bdev: Block device to clean buffers in
1643 * @block: Start of a range of blocks to clean
1644 * @len: Number of blocks to clean
1645 *
1646 * We are taking a range of blocks for data and we don't want writeback of any
1647 * buffer-cache aliases starting from return from this function and until the
1648 * moment when something will explicitly mark the buffer dirty (hopefully that
1649 * will not happen until we will free that block ;-) We don't even need to mark
1650 * it not-uptodate - nobody can expect anything from a newly allocated buffer
1651 * anyway. We used to use unmap_buffer() for such invalidation, but that was
1652 * wrong. We definitely don't want to mark the alias unmapped, for example - it
1653 * would confuse anyone who might pick it with bread() afterwards...
1654 *
1655 * Also.. Note that bforget() doesn't lock the buffer. So there can be
1656 * writeout I/O going on against recently-freed buffers. We don't wait on that
1657 * I/O in bforget() - it's more efficient to wait on the I/O only if we really
1658 * need to. That happens here.
1659 */
1660void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len)
1661{
1662 struct inode *bd_inode = bdev->bd_inode;
1663 struct address_space *bd_mapping = bd_inode->i_mapping;
1664 struct pagevec pvec;
1665 pgoff_t index = block >> (PAGE_SHIFT - bd_inode->i_blkbits);
1666 pgoff_t end;
1667 int i;
1668 struct buffer_head *bh;
1669 struct buffer_head *head;
1670
1671 end = (block + len - 1) >> (PAGE_SHIFT - bd_inode->i_blkbits);
1672 pagevec_init(&pvec, 0);
1673 while (index <= end && pagevec_lookup(&pvec, bd_mapping, index,
1674 min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
1675 for (i = 0; i < pagevec_count(&pvec); i++) {
1676 struct page *page = pvec.pages[i];
1677
1678 index = page->index;
1679 if (index > end)
1680 break;
1681 if (!page_has_buffers(page))
1682 continue;
1683 /*
1684 * We use page lock instead of bd_mapping->private_lock
1685 * to pin buffers here since we can afford to sleep and
1686 * it scales better than a global spinlock lock.
1687 */
1688 lock_page(page);
1689 /* Recheck when the page is locked which pins bhs */
1690 if (!page_has_buffers(page))
1691 goto unlock_page;
1692 head = page_buffers(page);
1693 bh = head;
1694 do {
1695 if (!buffer_mapped(bh))
1696 goto next;
1697 if (bh->b_blocknr >= block + len)
1698 break;
1699 clear_buffer_dirty(bh);
1700 wait_on_buffer(bh);
1701 clear_buffer_req(bh);
1702next:
1703 bh = bh->b_this_page;
1704 } while (bh != head);
1705unlock_page:
1706 unlock_page(page);
1707 }
1708 pagevec_release(&pvec);
1709 cond_resched();
1710 index++;
1711 }
1712}
1713EXPORT_SYMBOL(clean_bdev_aliases);
1714
1639/* 1715/*
1640 * Size is a power-of-two in the range 512..PAGE_SIZE, 1716 * Size is a power-of-two in the range 512..PAGE_SIZE,
1641 * and the case we care about most is PAGE_SIZE. 1717 * and the case we care about most is PAGE_SIZE.