Skip to content

Commit 763e4cd

Browse files
Brian Fosterdjwong
authored andcommitted
iomap: support partial page discard on writeback block mapping failure
iomap writeback mapping failure only calls into ->discard_page() if the current page has not been added to the ioend. Accordingly, the XFS callback assumes a full page discard and invalidation. This is problematic for sub-page block size filesystems where some portion of a page might have been mapped successfully before a failure to map a delalloc block occurs. ->discard_page() is not called in that error scenario and the bio is explicitly failed by iomap via the error return from ->prepare_ioend(). As a result, the filesystem leaks delalloc blocks and corrupts the filesystem block counters. Since XFS is the only user of ->discard_page(), tweak the semantics to invoke the callback unconditionally on mapping errors and provide the file offset that failed to map. Update xfs_discard_page() to discard the corresponding portion of the file and pass the range along to iomap_invalidatepage(). The latter already properly handles both full and sub-page scenarios by not changing any iomap or page state on sub-page invalidations. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
1 parent 869ae85 commit 763e4cd

3 files changed

Lines changed: 17 additions & 14 deletions

File tree

fs/iomap/buffered-io.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,14 +1382,15 @@ iomap_writepage_map(struct iomap_writepage_ctx *wpc,
13821382
* appropriately.
13831383
*/
13841384
if (unlikely(error)) {
1385+
/*
1386+
* Let the filesystem know what portion of the current page
1387+
* failed to map. If the page wasn't been added to ioend, it
1388+
* won't be affected by I/O completion and we must unlock it
1389+
* now.
1390+
*/
1391+
if (wpc->ops->discard_page)
1392+
wpc->ops->discard_page(page, file_offset);
13851393
if (!count) {
1386-
/*
1387-
* If the current page hasn't been added to ioend, it
1388-
* won't be affected by I/O completions and we must
1389-
* discard and unlock it right here.
1390-
*/
1391-
if (wpc->ops->discard_page)
1392-
wpc->ops->discard_page(page);
13931394
ClearPageUptodate(page);
13941395
unlock_page(page);
13951396
goto done;

fs/xfs/xfs_aops.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -527,28 +527,30 @@ xfs_prepare_ioend(
527527
*/
528528
static void
529529
xfs_discard_page(
530-
struct page *page)
530+
struct page *page,
531+
loff_t fileoff)
531532
{
532533
struct inode *inode = page->mapping->host;
533534
struct xfs_inode *ip = XFS_I(inode);
534535
struct xfs_mount *mp = ip->i_mount;
535-
loff_t offset = page_offset(page);
536-
xfs_fileoff_t start_fsb = XFS_B_TO_FSBT(mp, offset);
536+
unsigned int pageoff = offset_in_page(fileoff);
537+
xfs_fileoff_t start_fsb = XFS_B_TO_FSBT(mp, fileoff);
538+
xfs_fileoff_t pageoff_fsb = XFS_B_TO_FSBT(mp, pageoff);
537539
int error;
538540

539541
if (XFS_FORCED_SHUTDOWN(mp))
540542
goto out_invalidate;
541543

542544
xfs_alert_ratelimited(mp,
543545
"page discard on page "PTR_FMT", inode 0x%llx, offset %llu.",
544-
page, ip->i_ino, offset);
546+
page, ip->i_ino, fileoff);
545547

546548
error = xfs_bmap_punch_delalloc_range(ip, start_fsb,
547-
i_blocks_per_page(inode, page));
549+
i_blocks_per_page(inode, page) - pageoff_fsb);
548550
if (error && !XFS_FORCED_SHUTDOWN(mp))
549551
xfs_alert(mp, "page discard unable to remove delalloc mapping.");
550552
out_invalidate:
551-
iomap_invalidatepage(page, 0, PAGE_SIZE);
553+
iomap_invalidatepage(page, pageoff, PAGE_SIZE - pageoff);
552554
}
553555

554556
static const struct iomap_writeback_ops xfs_writeback_ops = {

include/linux/iomap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ struct iomap_writeback_ops {
221221
* Optional, allows the file system to discard state on a page where
222222
* we failed to submit any I/O.
223223
*/
224-
void (*discard_page)(struct page *page);
224+
void (*discard_page)(struct page *page, loff_t fileoff);
225225
};
226226

227227
struct iomap_writepage_ctx {

0 commit comments

Comments
 (0)