Merge tag 'folio-5.18b' of git://git.infradead.org/users/willy/pagecache
Pull filesystem folio updates from Matthew Wilcox:
"Primarily this series converts some of the address_space operations to
take a folio instead of a page.
Notably:
- a_ops->is_partially_uptodate() takes a folio instead of a page and
changes the type of the 'from' and 'count' arguments to make it
obvious they're bytes.
- a_ops->invalidatepage() becomes ->invalidate_folio() and has a
similar type change.
- a_ops->launder_page() becomes ->launder_folio()
- a_ops->set_page_dirty() becomes ->dirty_folio() and adds the
address_space as an argument.
There are a couple of other misc changes up front that weren't worth
separating into their own pull request"
* tag 'folio-5.18b' of git://git.infradead.org/users/willy/pagecache: (53 commits)
fs: Remove aops ->set_page_dirty
fb_defio: Use noop_dirty_folio()
fs: Convert __set_page_dirty_no_writeback to noop_dirty_folio
fs: Convert __set_page_dirty_buffers to block_dirty_folio
nilfs: Convert nilfs_set_page_dirty() to nilfs_dirty_folio()
mm: Convert swap_set_page_dirty() to swap_dirty_folio()
ubifs: Convert ubifs_set_page_dirty to ubifs_dirty_folio
f2fs: Convert f2fs_set_node_page_dirty to f2fs_dirty_node_folio
f2fs: Convert f2fs_set_data_page_dirty to f2fs_dirty_data_folio
f2fs: Convert f2fs_set_meta_page_dirty to f2fs_dirty_meta_folio
afs: Convert afs_dir_set_page_dirty() to afs_dir_dirty_folio()
btrfs: Convert extent_range_redirty_for_io() to use folios
fs: Convert trivial uses of __set_page_dirty_nobuffers to filemap_dirty_folio
btrfs: Convert from set_page_dirty to dirty_folio
fscache: Convert fscache_set_page_dirty() to fscache_dirty_folio()
fs: Add aops->dirty_folio
fs: Remove aops->launder_page
orangefs: Convert launder_page to launder_folio
nfs: Convert from launder_page to launder_folio
fuse: Convert from launder_page to launder_folio
...
This commit is contained in:
96
fs/buffer.c
96
fs/buffer.c
@@ -613,17 +613,14 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode);
|
||||
* FIXME: may need to call ->reservepage here as well. That's rather up to the
|
||||
* address_space though.
|
||||
*/
|
||||
int __set_page_dirty_buffers(struct page *page)
|
||||
bool block_dirty_folio(struct address_space *mapping, struct folio *folio)
|
||||
{
|
||||
int newly_dirty;
|
||||
struct address_space *mapping = page_mapping(page);
|
||||
|
||||
if (unlikely(!mapping))
|
||||
return !TestSetPageDirty(page);
|
||||
struct buffer_head *head;
|
||||
bool newly_dirty;
|
||||
|
||||
spin_lock(&mapping->private_lock);
|
||||
if (page_has_buffers(page)) {
|
||||
struct buffer_head *head = page_buffers(page);
|
||||
head = folio_buffers(folio);
|
||||
if (head) {
|
||||
struct buffer_head *bh = head;
|
||||
|
||||
do {
|
||||
@@ -635,21 +632,21 @@ int __set_page_dirty_buffers(struct page *page)
|
||||
* Lock out page's memcg migration to keep PageDirty
|
||||
* synchronized with per-memcg dirty page counters.
|
||||
*/
|
||||
lock_page_memcg(page);
|
||||
newly_dirty = !TestSetPageDirty(page);
|
||||
folio_memcg_lock(folio);
|
||||
newly_dirty = !folio_test_set_dirty(folio);
|
||||
spin_unlock(&mapping->private_lock);
|
||||
|
||||
if (newly_dirty)
|
||||
__set_page_dirty(page, mapping, 1);
|
||||
__folio_mark_dirty(folio, mapping, 1);
|
||||
|
||||
unlock_page_memcg(page);
|
||||
folio_memcg_unlock(folio);
|
||||
|
||||
if (newly_dirty)
|
||||
__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
|
||||
|
||||
return newly_dirty;
|
||||
}
|
||||
EXPORT_SYMBOL(__set_page_dirty_buffers);
|
||||
EXPORT_SYMBOL(block_dirty_folio);
|
||||
|
||||
/*
|
||||
* Write out and wait upon a list of buffers.
|
||||
@@ -1484,41 +1481,40 @@ static void discard_buffer(struct buffer_head * bh)
|
||||
}
|
||||
|
||||
/**
|
||||
* block_invalidatepage - invalidate part or all of a buffer-backed page
|
||||
*
|
||||
* @page: the page which is affected
|
||||
* block_invalidate_folio - Invalidate part or all of a buffer-backed folio.
|
||||
* @folio: The folio which is affected.
|
||||
* @offset: start of the range to invalidate
|
||||
* @length: length of the range to invalidate
|
||||
*
|
||||
* block_invalidatepage() is called when all or part of the page has become
|
||||
* block_invalidate_folio() is called when all or part of the folio has been
|
||||
* invalidated by a truncate operation.
|
||||
*
|
||||
* block_invalidatepage() does not have to release all buffers, but it must
|
||||
* block_invalidate_folio() does not have to release all buffers, but it must
|
||||
* ensure that no dirty buffer is left outside @offset and that no I/O
|
||||
* is underway against any of the blocks which are outside the truncation
|
||||
* point. Because the caller is about to free (and possibly reuse) those
|
||||
* blocks on-disk.
|
||||
*/
|
||||
void block_invalidatepage(struct page *page, unsigned int offset,
|
||||
unsigned int length)
|
||||
void block_invalidate_folio(struct folio *folio, size_t offset, size_t length)
|
||||
{
|
||||
struct buffer_head *head, *bh, *next;
|
||||
unsigned int curr_off = 0;
|
||||
unsigned int stop = length + offset;
|
||||
size_t curr_off = 0;
|
||||
size_t stop = length + offset;
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
if (!page_has_buffers(page))
|
||||
goto out;
|
||||
BUG_ON(!folio_test_locked(folio));
|
||||
|
||||
/*
|
||||
* Check for overflow
|
||||
*/
|
||||
BUG_ON(stop > PAGE_SIZE || stop < length);
|
||||
BUG_ON(stop > folio_size(folio) || stop < length);
|
||||
|
||||
head = folio_buffers(folio);
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
head = page_buffers(page);
|
||||
bh = head;
|
||||
do {
|
||||
unsigned int next_off = curr_off + bh->b_size;
|
||||
size_t next_off = curr_off + bh->b_size;
|
||||
next = bh->b_this_page;
|
||||
|
||||
/*
|
||||
@@ -1537,21 +1533,21 @@ void block_invalidatepage(struct page *page, unsigned int offset,
|
||||
} while (bh != head);
|
||||
|
||||
/*
|
||||
* We release buffers only if the entire page is being invalidated.
|
||||
* We release buffers only if the entire folio is being invalidated.
|
||||
* The get_block cached value has been unconditionally invalidated,
|
||||
* so real IO is not possible anymore.
|
||||
*/
|
||||
if (length == PAGE_SIZE)
|
||||
try_to_release_page(page, 0);
|
||||
if (length == folio_size(folio))
|
||||
filemap_release_folio(folio, 0);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(block_invalidatepage);
|
||||
EXPORT_SYMBOL(block_invalidate_folio);
|
||||
|
||||
|
||||
/*
|
||||
* We attach and possibly dirty the buffers atomically wrt
|
||||
* __set_page_dirty_buffers() via private_lock. try_to_free_buffers
|
||||
* block_dirty_folio() via private_lock. try_to_free_buffers
|
||||
* is already excluded via the page lock.
|
||||
*/
|
||||
void create_empty_buffers(struct page *page,
|
||||
@@ -1726,12 +1722,12 @@ int __block_write_full_page(struct inode *inode, struct page *page,
|
||||
(1 << BH_Dirty)|(1 << BH_Uptodate));
|
||||
|
||||
/*
|
||||
* Be very careful. We have no exclusion from __set_page_dirty_buffers
|
||||
* Be very careful. We have no exclusion from block_dirty_folio
|
||||
* here, and the (potentially unmapped) buffers may become dirty at
|
||||
* any time. If a buffer becomes dirty here after we've inspected it
|
||||
* then we just miss that fact, and the page stays dirty.
|
||||
*
|
||||
* Buffers outside i_size may be dirtied by __set_page_dirty_buffers;
|
||||
* Buffers outside i_size may be dirtied by block_dirty_folio;
|
||||
* handle that here by just cleaning them.
|
||||
*/
|
||||
|
||||
@@ -2208,29 +2204,27 @@ int generic_write_end(struct file *file, struct address_space *mapping,
|
||||
EXPORT_SYMBOL(generic_write_end);
|
||||
|
||||
/*
|
||||
* block_is_partially_uptodate checks whether buffers within a page are
|
||||
* block_is_partially_uptodate checks whether buffers within a folio are
|
||||
* uptodate or not.
|
||||
*
|
||||
* Returns true if all buffers which correspond to a file portion
|
||||
* we want to read are uptodate.
|
||||
* Returns true if all buffers which correspond to the specified part
|
||||
* of the folio are uptodate.
|
||||
*/
|
||||
int block_is_partially_uptodate(struct page *page, unsigned long from,
|
||||
unsigned long count)
|
||||
bool block_is_partially_uptodate(struct folio *folio, size_t from, size_t count)
|
||||
{
|
||||
unsigned block_start, block_end, blocksize;
|
||||
unsigned to;
|
||||
struct buffer_head *bh, *head;
|
||||
int ret = 1;
|
||||
bool ret = true;
|
||||
|
||||
if (!page_has_buffers(page))
|
||||
return 0;
|
||||
|
||||
head = page_buffers(page);
|
||||
head = folio_buffers(folio);
|
||||
if (!head)
|
||||
return false;
|
||||
blocksize = head->b_size;
|
||||
to = min_t(unsigned, PAGE_SIZE - from, count);
|
||||
to = min_t(unsigned, folio_size(folio) - from, count);
|
||||
to = from + to;
|
||||
if (from < blocksize && to > PAGE_SIZE - blocksize)
|
||||
return 0;
|
||||
if (from < blocksize && to > folio_size(folio) - blocksize)
|
||||
return false;
|
||||
|
||||
bh = head;
|
||||
block_start = 0;
|
||||
@@ -2238,7 +2232,7 @@ int block_is_partially_uptodate(struct page *page, unsigned long from,
|
||||
block_end = block_start + blocksize;
|
||||
if (block_end > from && block_start < to) {
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ret = 0;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
if (block_end >= to)
|
||||
@@ -3185,7 +3179,7 @@ EXPORT_SYMBOL(sync_dirty_buffer);
|
||||
*
|
||||
* The same applies to regular filesystem pages: if all the buffers are
|
||||
* clean then we set the page clean and proceed. To do that, we require
|
||||
* total exclusion from __set_page_dirty_buffers(). That is obtained with
|
||||
* total exclusion from block_dirty_folio(). That is obtained with
|
||||
* private_lock.
|
||||
*
|
||||
* try_to_free_buffers() is non-blocking.
|
||||
@@ -3252,7 +3246,7 @@ int try_to_free_buffers(struct page *page)
|
||||
* the page also.
|
||||
*
|
||||
* private_lock must be held over this entire operation in order
|
||||
* to synchronise against __set_page_dirty_buffers and prevent the
|
||||
* to synchronise against block_dirty_folio and prevent the
|
||||
* dirty bit from being lost.
|
||||
*/
|
||||
if (ret)
|
||||
|
||||
Reference in New Issue
Block a user