Skip to content

Commit d47992f

Browse files
Lukas Czernertytso
authored andcommitted
mm: change invalidatepage prototype to accept length
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 <lczerner@redhat.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Hugh Dickins <hughd@google.com>
1 parent c778879 commit d47992f

File tree

30 files changed

+116
-69
lines changed

30 files changed

+116
-69
lines changed

Documentation/filesystems/Locking

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ prototypes:
189189
loff_t pos, unsigned len, unsigned copied,
190190
struct page *page, void *fsdata);
191191
sector_t (*bmap)(struct address_space *, sector_t);
192-
int (*invalidatepage) (struct page *, unsigned long);
192+
void (*invalidatepage) (struct page *, unsigned int, unsigned int);
193193
int (*releasepage) (struct page *, int);
194194
void (*freepage)(struct page *);
195195
int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
@@ -310,8 +310,8 @@ filesystems and by the swapper. The latter will eventually go away. Please,
310310
keep it that way and don't breed new callers.
311311

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

317317
->releasepage() is called when the kernel is about to try to drop the

Documentation/filesystems/vfs.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ struct address_space_operations
549549
-------------------------------
550550

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

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

697697
releasepage: releasepage is called on PagePrivate pages to indicate
698698
that the page should be freed if possible. ->releasepage

fs/9p/vfs_addr.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,14 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)
148148
* @offset: offset in the page
149149
*/
150150

151-
static void v9fs_invalidate_page(struct page *page, unsigned long offset)
151+
static void v9fs_invalidate_page(struct page *page, unsigned int offset,
152+
unsigned int length)
152153
{
153154
/*
154155
* If called with zero offset, we should release
155156
* the private state assocated with the page
156157
*/
157-
if (offset == 0)
158+
if (offset == 0 && length == PAGE_CACHE_SIZE)
158159
v9fs_fscache_invalidate_page(page);
159160
}
160161

fs/afs/file.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
#include "internal.h"
2020

2121
static int afs_readpage(struct file *file, struct page *page);
22-
static void afs_invalidatepage(struct page *page, unsigned long offset);
22+
static void afs_invalidatepage(struct page *page, unsigned int offset,
23+
unsigned int length);
2324
static int afs_releasepage(struct page *page, gfp_t gfp_flags);
2425
static int afs_launder_page(struct page *page);
2526

@@ -310,16 +311,17 @@ static int afs_launder_page(struct page *page)
310311
* - release a page and clean up its private data if offset is 0 (indicating
311312
* the entire page)
312313
*/
313-
static void afs_invalidatepage(struct page *page, unsigned long offset)
314+
static void afs_invalidatepage(struct page *page, unsigned int offset,
315+
unsigned int length)
314316
{
315317
struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
316318

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

319321
BUG_ON(!PageLocked(page));
320322

321323
/* we clean up only if the entire page is being invalidated */
322-
if (offset == 0) {
324+
if (offset == 0 && length == PAGE_CACHE_SIZE) {
323325
#ifdef CONFIG_AFS_FSCACHE
324326
if (PageFsCache(page)) {
325327
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);

fs/btrfs/disk-io.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,8 @@ static int btree_releasepage(struct page *page, gfp_t gfp_flags)
10131013
return try_release_extent_buffer(page);
10141014
}
10151015

1016-
static void btree_invalidatepage(struct page *page, unsigned long offset)
1016+
static void btree_invalidatepage(struct page *page, unsigned int offset,
1017+
unsigned int length)
10171018
{
10181019
struct extent_io_tree *tree;
10191020
tree = &BTRFS_I(page->mapping->host)->io_tree;

fs/btrfs/extent_io.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2957,7 +2957,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
29572957
pg_offset = i_size & (PAGE_CACHE_SIZE - 1);
29582958
if (page->index > end_index ||
29592959
(page->index == end_index && !pg_offset)) {
2960-
page->mapping->a_ops->invalidatepage(page, 0);
2960+
page->mapping->a_ops->invalidatepage(page, 0, PAGE_CACHE_SIZE);
29612961
unlock_page(page);
29622962
return 0;
29632963
}

fs/btrfs/inode.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7510,7 +7510,8 @@ static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
75107510
return __btrfs_releasepage(page, gfp_flags & GFP_NOFS);
75117511
}
75127512

7513-
static void btrfs_invalidatepage(struct page *page, unsigned long offset)
7513+
static void btrfs_invalidatepage(struct page *page, unsigned int offset,
7514+
unsigned int length)
75147515
{
75157516
struct inode *inode = page->mapping->host;
75167517
struct extent_io_tree *tree;

fs/buffer.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,8 @@ static void discard_buffer(struct buffer_head * bh)
14541454
* block_invalidatepage - invalidate part or all of a buffer-backed page
14551455
*
14561456
* @page: the page which is affected
1457-
* @offset: the index of the truncation point
1457+
* @offset: start of the range to invalidate
1458+
* @length: length of the range to invalidate
14581459
*
14591460
* block_invalidatepage() is called when all or part of the page has become
14601461
* invalidated by a truncate operation.
@@ -1465,21 +1466,34 @@ static void discard_buffer(struct buffer_head * bh)
14651466
* point. Because the caller is about to free (and possibly reuse) those
14661467
* blocks on-disk.
14671468
*/
1468-
void block_invalidatepage(struct page *page, unsigned long offset)
1469+
void block_invalidatepage(struct page *page, unsigned int offset,
1470+
unsigned int length)
14691471
{
14701472
struct buffer_head *head, *bh, *next;
14711473
unsigned int curr_off = 0;
1474+
unsigned int stop = length + offset;
14721475

14731476
BUG_ON(!PageLocked(page));
14741477
if (!page_has_buffers(page))
14751478
goto out;
14761479

1480+
/*
1481+
* Check for overflow
1482+
*/
1483+
BUG_ON(stop > PAGE_CACHE_SIZE || stop < length);
1484+
14771485
head = page_buffers(page);
14781486
bh = head;
14791487
do {
14801488
unsigned int next_off = curr_off + bh->b_size;
14811489
next = bh->b_this_page;
14821490

1491+
/*
1492+
* Are we still fully in range ?
1493+
*/
1494+
if (next_off > stop)
1495+
goto out;
1496+
14831497
/*
14841498
* is this block fully invalidated?
14851499
*/
@@ -1501,6 +1515,7 @@ void block_invalidatepage(struct page *page, unsigned long offset)
15011515
}
15021516
EXPORT_SYMBOL(block_invalidatepage);
15031517

1518+
15041519
/*
15051520
* We attach and possibly dirty the buffers atomically wrt
15061521
* __set_page_dirty_buffers() via private_lock. try_to_free_buffers
@@ -2841,7 +2856,7 @@ int block_write_full_page_endio(struct page *page, get_block_t *get_block,
28412856
* they may have been added in ext3_writepage(). Make them
28422857
* freeable here, so the page does not leak.
28432858
*/
2844-
do_invalidatepage(page, 0);
2859+
do_invalidatepage(page, 0, PAGE_CACHE_SIZE);
28452860
unlock_page(page);
28462861
return 0; /* don't care */
28472862
}

fs/ceph/addr.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ static int ceph_set_page_dirty(struct page *page)
143143
* dirty page counters appropriately. Only called if there is private
144144
* data on the page.
145145
*/
146-
static void ceph_invalidatepage(struct page *page, unsigned long offset)
146+
static void ceph_invalidatepage(struct page *page, unsigned int offset,
147+
unsigned int length)
147148
{
148149
struct inode *inode;
149150
struct ceph_inode_info *ci;
@@ -168,7 +169,7 @@ static void ceph_invalidatepage(struct page *page, unsigned long offset)
168169

169170
ci = ceph_inode(inode);
170171
if (offset == 0) {
171-
dout("%p invalidatepage %p idx %lu full dirty page %lu\n",
172+
dout("%p invalidatepage %p idx %lu full dirty page %u\n",
172173
inode, page, page->index, offset);
173174
ceph_put_wrbuffer_cap_refs(ci, 1, snapc);
174175
ceph_put_snap_context(snapc);

fs/cifs/file.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,11 +3546,12 @@ static int cifs_release_page(struct page *page, gfp_t gfp)
35463546
return cifs_fscache_release_page(page, gfp);
35473547
}
35483548

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

3553-
if (offset == 0)
3554+
if (offset == 0 && length == PAGE_CACHE_SIZE)
35543555
cifs_fscache_invalidate_page(page, &cifsi->vfs_inode);
35553556
}
35563557

0 commit comments

Comments
 (0)