Skip to content

Commit

Permalink
mm: change invalidatepage prototype to accept length
Browse files Browse the repository at this point in the history
Currently there is no way to truncate partial page where the end
truncate point is not at the end of the page. This is because it was not
needed and the functionality was enough for file system truncate
operation to work properly. However more file systems now support punch
hole feature and it can benefit from mm supporting truncating page just
up to the certain point.

Specifically, with this functionality truncate_inode_pages_range() can
be changed so it supports truncating partial page at the end of the
range (currently it will BUG_ON() if 'end' is not at the end of the
page).

This commit changes the invalidatepage() address space operation
prototype to accept range to be invalidated and update all the instances
for it.

We also change the block_invalidatepage() in the same way and actually
make a use of the new length argument implementing range invalidation.

Actual file system implementations will follow except the file systems
where the changes are really simple and should not change the behaviour
in any way .Implementation for truncate_page_range() which will be able
to accept page unaligned ranges will follow as well.

Signed-off-by: Lukas Czerner <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Hugh Dickins <[email protected]>
  • Loading branch information
Lukas Czerner authored and tytso committed May 22, 2013
1 parent c778879 commit d47992f
Show file tree
Hide file tree
Showing 30 changed files with 116 additions and 69 deletions.
6 changes: 3 additions & 3 deletions Documentation/filesystems/Locking
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ prototypes:
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata);
sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long);
void (*invalidatepage) (struct page *, unsigned int, unsigned int);
int (*releasepage) (struct page *, int);
void (*freepage)(struct page *);
int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
Expand Down Expand Up @@ -310,8 +310,8 @@ filesystems and by the swapper. The latter will eventually go away. Please,
keep it that way and don't breed new callers.

->invalidatepage() is called when the filesystem must attempt to drop
some or all of the buffers from the page when it is being truncated. It
returns zero on success. If ->invalidatepage is zero, the kernel uses
some or all of the buffers from the page when it is being truncated. It
returns zero on success. If ->invalidatepage is zero, the kernel uses
block_invalidatepage() instead.

->releasepage() is called when the kernel is about to try to drop the
Expand Down
20 changes: 10 additions & 10 deletions Documentation/filesystems/vfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ struct address_space_operations
-------------------------------

This describes how the VFS can manipulate mapping of a file to page cache in
your filesystem. As of kernel 2.6.22, the following members are defined:
your filesystem. The following members are defined:

struct address_space_operations {
int (*writepage)(struct page *page, struct writeback_control *wbc);
Expand All @@ -566,7 +566,7 @@ struct address_space_operations {
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata);
sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long);
void (*invalidatepage) (struct page *, unsigned int, unsigned int);
int (*releasepage) (struct page *, int);
void (*freepage)(struct page *);
ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
Expand Down Expand Up @@ -685,14 +685,14 @@ struct address_space_operations {
invalidatepage: If a page has PagePrivate set, then invalidatepage
will be called when part or all of the page is to be removed
from the address space. This generally corresponds to either a
truncation or a complete invalidation of the address space
(in the latter case 'offset' will always be 0).
Any private data associated with the page should be updated
to reflect this truncation. If offset is 0, then
the private data should be released, because the page
must be able to be completely discarded. This may be done by
calling the ->releasepage function, but in this case the
release MUST succeed.
truncation, punch hole or a complete invalidation of the address
space (in the latter case 'offset' will always be 0 and 'length'
will be PAGE_CACHE_SIZE). Any private data associated with the page
should be updated to reflect this truncation. If offset is 0 and
length is PAGE_CACHE_SIZE, then the private data should be released,
because the page must be able to be completely discarded. This may
be done by calling the ->releasepage function, but in this case the
release MUST succeed.

releasepage: releasepage is called on PagePrivate pages to indicate
that the page should be freed if possible. ->releasepage
Expand Down
5 changes: 3 additions & 2 deletions fs/9p/vfs_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,14 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)
* @offset: offset in the page
*/

static void v9fs_invalidate_page(struct page *page, unsigned long offset)
static void v9fs_invalidate_page(struct page *page, unsigned int offset,
unsigned int length)
{
/*
* If called with zero offset, we should release
* the private state assocated with the page
*/
if (offset == 0)
if (offset == 0 && length == PAGE_CACHE_SIZE)
v9fs_fscache_invalidate_page(page);
}

Expand Down
10 changes: 6 additions & 4 deletions fs/afs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#include "internal.h"

static int afs_readpage(struct file *file, struct page *page);
static void afs_invalidatepage(struct page *page, unsigned long offset);
static void afs_invalidatepage(struct page *page, unsigned int offset,
unsigned int length);
static int afs_releasepage(struct page *page, gfp_t gfp_flags);
static int afs_launder_page(struct page *page);

Expand Down Expand Up @@ -310,16 +311,17 @@ static int afs_launder_page(struct page *page)
* - release a page and clean up its private data if offset is 0 (indicating
* the entire page)
*/
static void afs_invalidatepage(struct page *page, unsigned long offset)
static void afs_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct afs_writeback *wb = (struct afs_writeback *) page_private(page);

_enter("{%lu},%lu", page->index, offset);
_enter("{%lu},%u,%u", page->index, offset, length);

BUG_ON(!PageLocked(page));

/* we clean up only if the entire page is being invalidated */
if (offset == 0) {
if (offset == 0 && length == PAGE_CACHE_SIZE) {
#ifdef CONFIG_AFS_FSCACHE
if (PageFsCache(page)) {
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
Expand Down
3 changes: 2 additions & 1 deletion fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,8 @@ static int btree_releasepage(struct page *page, gfp_t gfp_flags)
return try_release_extent_buffer(page);
}

static void btree_invalidatepage(struct page *page, unsigned long offset)
static void btree_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct extent_io_tree *tree;
tree = &BTRFS_I(page->mapping->host)->io_tree;
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2957,7 +2957,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
pg_offset = i_size & (PAGE_CACHE_SIZE - 1);
if (page->index > end_index ||
(page->index == end_index && !pg_offset)) {
page->mapping->a_ops->invalidatepage(page, 0);
page->mapping->a_ops->invalidatepage(page, 0, PAGE_CACHE_SIZE);
unlock_page(page);
return 0;
}
Expand Down
3 changes: 2 additions & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -7510,7 +7510,8 @@ static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
return __btrfs_releasepage(page, gfp_flags & GFP_NOFS);
}

static void btrfs_invalidatepage(struct page *page, unsigned long offset)
static void btrfs_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct inode *inode = page->mapping->host;
struct extent_io_tree *tree;
Expand Down
21 changes: 18 additions & 3 deletions fs/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1454,7 +1454,8 @@ 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
* @offset: the index of the truncation point
* @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
* invalidated by a truncate operation.
Expand All @@ -1465,21 +1466,34 @@ static void discard_buffer(struct buffer_head * bh)
* point. Because the caller is about to free (and possibly reuse) those
* blocks on-disk.
*/
void block_invalidatepage(struct page *page, unsigned long offset)
void block_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct buffer_head *head, *bh, *next;
unsigned int curr_off = 0;
unsigned int stop = length + offset;

BUG_ON(!PageLocked(page));
if (!page_has_buffers(page))
goto out;

/*
* Check for overflow
*/
BUG_ON(stop > PAGE_CACHE_SIZE || stop < length);

head = page_buffers(page);
bh = head;
do {
unsigned int next_off = curr_off + bh->b_size;
next = bh->b_this_page;

/*
* Are we still fully in range ?
*/
if (next_off > stop)
goto out;

/*
* is this block fully invalidated?
*/
Expand All @@ -1501,6 +1515,7 @@ void block_invalidatepage(struct page *page, unsigned long offset)
}
EXPORT_SYMBOL(block_invalidatepage);


/*
* We attach and possibly dirty the buffers atomically wrt
* __set_page_dirty_buffers() via private_lock. try_to_free_buffers
Expand Down Expand Up @@ -2841,7 +2856,7 @@ int block_write_full_page_endio(struct page *page, get_block_t *get_block,
* they may have been added in ext3_writepage(). Make them
* freeable here, so the page does not leak.
*/
do_invalidatepage(page, 0);
do_invalidatepage(page, 0, PAGE_CACHE_SIZE);
unlock_page(page);
return 0; /* don't care */
}
Expand Down
5 changes: 3 additions & 2 deletions fs/ceph/addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ static int ceph_set_page_dirty(struct page *page)
* dirty page counters appropriately. Only called if there is private
* data on the page.
*/
static void ceph_invalidatepage(struct page *page, unsigned long offset)
static void ceph_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct inode *inode;
struct ceph_inode_info *ci;
Expand All @@ -168,7 +169,7 @@ static void ceph_invalidatepage(struct page *page, unsigned long offset)

ci = ceph_inode(inode);
if (offset == 0) {
dout("%p invalidatepage %p idx %lu full dirty page %lu\n",
dout("%p invalidatepage %p idx %lu full dirty page %u\n",
inode, page, page->index, offset);
ceph_put_wrbuffer_cap_refs(ci, 1, snapc);
ceph_put_snap_context(snapc);
Expand Down
5 changes: 3 additions & 2 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -3546,11 +3546,12 @@ static int cifs_release_page(struct page *page, gfp_t gfp)
return cifs_fscache_release_page(page, gfp);
}

static void cifs_invalidate_page(struct page *page, unsigned long offset)
static void cifs_invalidate_page(struct page *page, unsigned int offset,
unsigned int length)
{
struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host);

if (offset == 0)
if (offset == 0 && length == PAGE_CACHE_SIZE)
cifs_fscache_invalidate_page(page, &cifsi->vfs_inode);
}

Expand Down
6 changes: 4 additions & 2 deletions fs/exofs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -953,9 +953,11 @@ static int exofs_releasepage(struct page *page, gfp_t gfp)
return 0;
}

static void exofs_invalidatepage(struct page *page, unsigned long offset)
static void exofs_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
EXOFS_DBGMSG("page 0x%lx offset 0x%lx\n", page->index, offset);
EXOFS_DBGMSG("page 0x%lx offset 0x%x length 0x%x\n",
page->index, offset, length);
WARN_ON(1);
}

Expand Down
3 changes: 2 additions & 1 deletion fs/ext3/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,8 @@ ext3_readpages(struct file *file, struct address_space *mapping,
return mpage_readpages(mapping, pages, nr_pages, ext3_get_block);
}

static void ext3_invalidatepage(struct page *page, unsigned long offset)
static void ext3_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
journal_t *journal = EXT3_JOURNAL(page->mapping->host);

Expand Down
18 changes: 11 additions & 7 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ static inline int ext4_begin_ordered_truncate(struct inode *inode,
new_size);
}

static void ext4_invalidatepage(struct page *page, unsigned long offset);
static void ext4_invalidatepage(struct page *page, unsigned int offset,
unsigned int length);
static int __ext4_journalled_writepage(struct page *page, unsigned int len);
static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh);
static int ext4_discard_partial_page_buffers_no_lock(handle_t *handle,
Expand Down Expand Up @@ -1606,7 +1607,7 @@ static void ext4_da_block_invalidatepages(struct mpage_da_data *mpd)
break;
BUG_ON(!PageLocked(page));
BUG_ON(PageWriteback(page));
block_invalidatepage(page, 0);
block_invalidatepage(page, 0, PAGE_CACHE_SIZE);
ClearPageUptodate(page);
unlock_page(page);
}
Expand Down Expand Up @@ -2829,7 +2830,8 @@ static int ext4_da_write_end(struct file *file,
return ret ? ret : copied;
}

static void ext4_da_invalidatepage(struct page *page, unsigned long offset)
static void ext4_da_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
/*
* Drop reserved blocks
Expand All @@ -2841,7 +2843,7 @@ static void ext4_da_invalidatepage(struct page *page, unsigned long offset)
ext4_da_page_release_reservation(page, offset);

out:
ext4_invalidatepage(page, offset);
ext4_invalidatepage(page, offset, length);

return;
}
Expand Down Expand Up @@ -2989,14 +2991,15 @@ ext4_readpages(struct file *file, struct address_space *mapping,
return mpage_readpages(mapping, pages, nr_pages, ext4_get_block);
}

static void ext4_invalidatepage(struct page *page, unsigned long offset)
static void ext4_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
trace_ext4_invalidatepage(page, offset);

/* No journalling happens on data buffers when this function is used */
WARN_ON(page_has_buffers(page) && buffer_jbd(page_buffers(page)));

block_invalidatepage(page, offset);
block_invalidatepage(page, offset, PAGE_CACHE_SIZE - offset);
}

static int __ext4_journalled_invalidatepage(struct page *page,
Expand All @@ -3017,7 +3020,8 @@ static int __ext4_journalled_invalidatepage(struct page *page,

/* Wrapper for aops... */
static void ext4_journalled_invalidatepage(struct page *page,
unsigned long offset)
unsigned int offset,
unsigned int length)
{
WARN_ON(__ext4_journalled_invalidatepage(page, offset) < 0);
}
Expand Down
3 changes: 2 additions & 1 deletion fs/f2fs/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,8 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
get_data_block_ro);
}

static void f2fs_invalidate_data_page(struct page *page, unsigned long offset)
static void f2fs_invalidate_data_page(struct page *page, unsigned int offset,
unsigned int length)
{
struct inode *inode = page->mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
Expand Down
3 changes: 2 additions & 1 deletion fs/f2fs/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,8 @@ static int f2fs_set_node_page_dirty(struct page *page)
return 0;
}

static void f2fs_invalidate_node_page(struct page *page, unsigned long offset)
static void f2fs_invalidate_node_page(struct page *page, unsigned int offset,
unsigned int length)
{
struct inode *inode = page->mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
Expand Down
8 changes: 5 additions & 3 deletions fs/gfs2/aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ static int gfs2_writepage_common(struct page *page,
/* Is the page fully outside i_size? (truncate in progress) */
offset = i_size & (PAGE_CACHE_SIZE-1);
if (page->index > end_index || (page->index == end_index && !offset)) {
page->mapping->a_ops->invalidatepage(page, 0);
page->mapping->a_ops->invalidatepage(page, 0, PAGE_CACHE_SIZE);
goto out;
}
return 1;
Expand Down Expand Up @@ -299,7 +299,8 @@ static int gfs2_write_jdata_pagevec(struct address_space *mapping,

/* Is the page fully outside i_size? (truncate in progress) */
if (page->index > end_index || (page->index == end_index && !offset)) {
page->mapping->a_ops->invalidatepage(page, 0);
page->mapping->a_ops->invalidatepage(page, 0,
PAGE_CACHE_SIZE);
unlock_page(page);
continue;
}
Expand Down Expand Up @@ -943,7 +944,8 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh)
unlock_buffer(bh);
}

static void gfs2_invalidatepage(struct page *page, unsigned long offset)
static void gfs2_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
struct buffer_head *bh, *head;
Expand Down
5 changes: 3 additions & 2 deletions fs/jfs/jfs_metapage.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,10 @@ static int metapage_releasepage(struct page *page, gfp_t gfp_mask)
return ret;
}

static void metapage_invalidatepage(struct page *page, unsigned long offset)
static void metapage_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
BUG_ON(offset);
BUG_ON(offset || length < PAGE_CACHE_SIZE);

BUG_ON(PageWriteback(page));

Expand Down
3 changes: 2 additions & 1 deletion fs/logfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ static int logfs_writepage(struct page *page, struct writeback_control *wbc)
return __logfs_writepage(page);
}

static void logfs_invalidatepage(struct page *page, unsigned long offset)
static void logfs_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct logfs_block *block = logfs_block(page);

Expand Down
Loading

0 comments on commit d47992f

Please sign in to comment.