mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-26 08:31:13 +00:00
Merge branch 'for-4.10/fs-unmap' of git://git.kernel.dk/linux-block
Pull fs meta data unmap optimization from Jens Axboe: "A series from Jan Kara, providing a more efficient way for unmapping meta data from in the buffer cache than doing it block-by-block. Provide a general helper that existing callers can use" * 'for-4.10/fs-unmap' of git://git.kernel.dk/linux-block: fs: Remove unmap_underlying_metadata fs: Add helper to clean bdev aliases under a bh and use it ext2: Use clean_bdev_aliases() instead of iteration ext4: Use clean_bdev_aliases() instead of iteration direct-io: Use clean_bdev_aliases() instead of handmade iteration fs: Provide function to unmap metadata for a range of blocks
This commit is contained in:
commit
80eabba702
13 changed files with 102 additions and 93 deletions
100
fs/buffer.c
100
fs/buffer.c
|
@ -43,6 +43,7 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/mpage.h>
|
||||
#include <linux/bit_spinlock.h>
|
||||
#include <linux/pagevec.h>
|
||||
#include <trace/events/block.h>
|
||||
|
||||
static int fsync_buffers_list(spinlock_t *lock, struct list_head *list);
|
||||
|
@ -1604,37 +1605,80 @@ void create_empty_buffers(struct page *page,
|
|||
}
|
||||
EXPORT_SYMBOL(create_empty_buffers);
|
||||
|
||||
/*
|
||||
* We are taking a block for data and we don't want any output from any
|
||||
* buffer-cache aliases starting from return from that function and
|
||||
* until the moment when something will explicitly mark the buffer
|
||||
* dirty (hopefully that will not happen until we will free that block ;-)
|
||||
* We don't even need to mark it not-uptodate - nobody can expect
|
||||
* anything from a newly allocated buffer anyway. We used to used
|
||||
* unmap_buffer() for such invalidation, but that was wrong. We definitely
|
||||
* don't want to mark the alias unmapped, for example - it would confuse
|
||||
* anyone who might pick it with bread() afterwards...
|
||||
/**
|
||||
* clean_bdev_aliases: clean a range of buffers in block device
|
||||
* @bdev: Block device to clean buffers in
|
||||
* @block: Start of a range of blocks to clean
|
||||
* @len: Number of blocks to clean
|
||||
*
|
||||
* Also.. Note that bforget() doesn't lock the buffer. So there can
|
||||
* be writeout I/O going on against recently-freed buffers. We don't
|
||||
* wait on that I/O in bforget() - it's more efficient to wait on the I/O
|
||||
* only if we really need to. That happens here.
|
||||
* We are taking a range of blocks for data and we don't want writeback of any
|
||||
* buffer-cache aliases starting from return from this function and until the
|
||||
* moment when something will explicitly mark the buffer dirty (hopefully that
|
||||
* will not happen until we will free that block ;-) We don't even need to mark
|
||||
* it not-uptodate - nobody can expect anything from a newly allocated buffer
|
||||
* anyway. We used to use unmap_buffer() for such invalidation, but that was
|
||||
* wrong. We definitely don't want to mark the alias unmapped, for example - it
|
||||
* would confuse anyone who might pick it with bread() afterwards...
|
||||
*
|
||||
* Also.. Note that bforget() doesn't lock the buffer. So there can be
|
||||
* writeout I/O going on against recently-freed buffers. We don't wait on that
|
||||
* I/O in bforget() - it's more efficient to wait on the I/O only if we really
|
||||
* need to. That happens here.
|
||||
*/
|
||||
void unmap_underlying_metadata(struct block_device *bdev, sector_t block)
|
||||
void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len)
|
||||
{
|
||||
struct buffer_head *old_bh;
|
||||
struct inode *bd_inode = bdev->bd_inode;
|
||||
struct address_space *bd_mapping = bd_inode->i_mapping;
|
||||
struct pagevec pvec;
|
||||
pgoff_t index = block >> (PAGE_SHIFT - bd_inode->i_blkbits);
|
||||
pgoff_t end;
|
||||
int i;
|
||||
struct buffer_head *bh;
|
||||
struct buffer_head *head;
|
||||
|
||||
might_sleep();
|
||||
end = (block + len - 1) >> (PAGE_SHIFT - bd_inode->i_blkbits);
|
||||
pagevec_init(&pvec, 0);
|
||||
while (index <= end && pagevec_lookup(&pvec, bd_mapping, index,
|
||||
min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
|
||||
for (i = 0; i < pagevec_count(&pvec); i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
|
||||
old_bh = __find_get_block_slow(bdev, block);
|
||||
if (old_bh) {
|
||||
clear_buffer_dirty(old_bh);
|
||||
wait_on_buffer(old_bh);
|
||||
clear_buffer_req(old_bh);
|
||||
__brelse(old_bh);
|
||||
index = page->index;
|
||||
if (index > end)
|
||||
break;
|
||||
if (!page_has_buffers(page))
|
||||
continue;
|
||||
/*
|
||||
* We use page lock instead of bd_mapping->private_lock
|
||||
* to pin buffers here since we can afford to sleep and
|
||||
* it scales better than a global spinlock lock.
|
||||
*/
|
||||
lock_page(page);
|
||||
/* Recheck when the page is locked which pins bhs */
|
||||
if (!page_has_buffers(page))
|
||||
goto unlock_page;
|
||||
head = page_buffers(page);
|
||||
bh = head;
|
||||
do {
|
||||
if (!buffer_mapped(bh))
|
||||
goto next;
|
||||
if (bh->b_blocknr >= block + len)
|
||||
break;
|
||||
clear_buffer_dirty(bh);
|
||||
wait_on_buffer(bh);
|
||||
clear_buffer_req(bh);
|
||||
next:
|
||||
bh = bh->b_this_page;
|
||||
} while (bh != head);
|
||||
unlock_page:
|
||||
unlock_page(page);
|
||||
}
|
||||
pagevec_release(&pvec);
|
||||
cond_resched();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(unmap_underlying_metadata);
|
||||
EXPORT_SYMBOL(clean_bdev_aliases);
|
||||
|
||||
/*
|
||||
* Size is a power-of-two in the range 512..PAGE_SIZE,
|
||||
|
@ -1745,8 +1789,7 @@ int __block_write_full_page(struct inode *inode, struct page *page,
|
|||
if (buffer_new(bh)) {
|
||||
/* blockdev mappings never come here */
|
||||
clear_buffer_new(bh);
|
||||
unmap_underlying_metadata(bh->b_bdev,
|
||||
bh->b_blocknr);
|
||||
clean_bdev_bh_alias(bh);
|
||||
}
|
||||
}
|
||||
bh = bh->b_this_page;
|
||||
|
@ -1992,8 +2035,7 @@ int __block_write_begin_int(struct page *page, loff_t pos, unsigned len,
|
|||
}
|
||||
|
||||
if (buffer_new(bh)) {
|
||||
unmap_underlying_metadata(bh->b_bdev,
|
||||
bh->b_blocknr);
|
||||
clean_bdev_bh_alias(bh);
|
||||
if (PageUptodate(page)) {
|
||||
clear_buffer_new(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
|
@ -2633,7 +2675,7 @@ int nobh_write_begin(struct address_space *mapping,
|
|||
if (!buffer_mapped(bh))
|
||||
is_mapped_to_disk = 0;
|
||||
if (buffer_new(bh))
|
||||
unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
|
||||
clean_bdev_bh_alias(bh);
|
||||
if (PageUptodate(page)) {
|
||||
set_buffer_uptodate(bh);
|
||||
continue;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue