Skip to content

Commit b052035

Browse files
authored
Avoid BUG in migrate_folio_extra
Linux page migration code won't wait for writeback to complete unless it needs to call release_folio. Call SetPagePrivate wherever PageUptodate is set and define .release_folio, to cause fallback_migrate_folio to wait for us. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: tstabrawa <[email protected]> Closes openzfs#15140 Closes openzfs#16568
1 parent b2ca510 commit b052035

File tree

6 files changed

+140
-0
lines changed

6 files changed

+140
-0
lines changed

config/kernel-vfs-invalidate_folio.m4

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
dnl #
2+
dnl # Linux 5.18 uses invalidate_folio in lieu of invalidate_page
3+
dnl #
4+
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_INVALIDATE_FOLIO], [
5+
ZFS_LINUX_TEST_SRC([vfs_has_invalidate_folio], [
6+
#include <linux/fs.h>
7+
8+
static void
9+
test_invalidate_folio(struct folio *folio, size_t offset,
10+
size_t len) {
11+
(void) folio; (void) offset; (void) len;
12+
return;
13+
}
14+
15+
static const struct address_space_operations
16+
aops __attribute__ ((unused)) = {
17+
.invalidate_folio = test_invalidate_folio,
18+
};
19+
],[])
20+
])
21+
22+
AC_DEFUN([ZFS_AC_KERNEL_VFS_INVALIDATE_FOLIO], [
23+
dnl #
24+
dnl # Linux 5.18 uses invalidate_folio in lieu of invalidate_page
25+
dnl #
26+
AC_MSG_CHECKING([whether invalidate_folio exists])
27+
ZFS_LINUX_TEST_RESULT([vfs_has_invalidate_folio], [
28+
AC_MSG_RESULT([yes])
29+
AC_DEFINE(HAVE_VFS_INVALIDATE_FOLIO, 1, [invalidate_folio exists])
30+
],[
31+
AC_MSG_RESULT([no])
32+
])
33+
])

config/kernel-vfs-release_folio.m4

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
dnl #
2+
dnl # Linux 5.19 uses release_folio in lieu of releasepage
3+
dnl #
4+
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_RELEASE_FOLIO], [
5+
ZFS_LINUX_TEST_SRC([vfs_has_release_folio], [
6+
#include <linux/fs.h>
7+
8+
static bool
9+
test_release_folio(struct folio *folio, gfp_t gfp) {
10+
(void) folio; (void) gfp;
11+
return (0);
12+
}
13+
14+
static const struct address_space_operations
15+
aops __attribute__ ((unused)) = {
16+
.release_folio = test_release_folio,
17+
};
18+
],[])
19+
])
20+
21+
AC_DEFUN([ZFS_AC_KERNEL_VFS_RELEASE_FOLIO], [
22+
dnl #
23+
dnl # Linux 5.19 uses release_folio in lieu of releasepage
24+
dnl #
25+
AC_MSG_CHECKING([whether release_folio exists])
26+
ZFS_LINUX_TEST_RESULT([vfs_has_release_folio], [
27+
AC_MSG_RESULT([yes])
28+
AC_DEFINE(HAVE_VFS_RELEASE_FOLIO, 1, [release_folio exists])
29+
],[
30+
AC_MSG_RESULT([no])
31+
])
32+
])

config/kernel.m4

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
7777
ZFS_AC_KERNEL_SRC_SGET
7878
ZFS_AC_KERNEL_SRC_VFS_FILEMAP_DIRTY_FOLIO
7979
ZFS_AC_KERNEL_SRC_VFS_READ_FOLIO
80+
ZFS_AC_KERNEL_SRC_VFS_RELEASE_FOLIO
81+
ZFS_AC_KERNEL_SRC_VFS_INVALIDATE_FOLIO
8082
ZFS_AC_KERNEL_SRC_VFS_FSYNC_2ARGS
8183
ZFS_AC_KERNEL_SRC_VFS_DIRECT_IO
8284
ZFS_AC_KERNEL_SRC_VFS_READPAGES
@@ -185,6 +187,8 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
185187
ZFS_AC_KERNEL_SGET
186188
ZFS_AC_KERNEL_VFS_FILEMAP_DIRTY_FOLIO
187189
ZFS_AC_KERNEL_VFS_READ_FOLIO
190+
ZFS_AC_KERNEL_VFS_RELEASE_FOLIO
191+
ZFS_AC_KERNEL_VFS_INVALIDATE_FOLIO
188192
ZFS_AC_KERNEL_VFS_FSYNC_2ARGS
189193
ZFS_AC_KERNEL_VFS_DIRECT_IO
190194
ZFS_AC_KERNEL_VFS_READPAGES

module/os/linux/zfs/zfs_vnops_os.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ update_pages(znode_t *zp, int64_t start, int len, objset_t *os)
260260
} else {
261261
ClearPageError(pp);
262262
SetPageUptodate(pp);
263+
if (!PagePrivate(pp)) {
264+
/*
265+
* Set private bit so page migration
266+
* will wait for us to finish writeback
267+
* before calling migrate_folio().
268+
*/
269+
SetPagePrivate(pp);
270+
get_page(pp);
271+
}
263272

264273
if (mapping_writably_mapped(mp))
265274
flush_dcache_page(pp);
@@ -4037,6 +4046,14 @@ zfs_fillpage(struct inode *ip, struct page *pp)
40374046
} else {
40384047
ClearPageError(pp);
40394048
SetPageUptodate(pp);
4049+
if (!PagePrivate(pp)) {
4050+
/*
4051+
* Set private bit so page migration will wait for us to
4052+
* finish writeback before calling migrate_folio().
4053+
*/
4054+
SetPagePrivate(pp);
4055+
get_page(pp);
4056+
}
40404057
}
40414058

40424059
return (error);

module/os/linux/zfs/zfs_znode_os.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,14 @@ zfs_zero_partial_page(znode_t *zp, uint64_t start, uint64_t len)
15761576
mark_page_accessed(pp);
15771577
SetPageUptodate(pp);
15781578
ClearPageError(pp);
1579+
if (!PagePrivate(pp)) {
1580+
/*
1581+
* Set private bit so page migration will wait for us to
1582+
* finish writeback before calling migrate_folio().
1583+
*/
1584+
SetPagePrivate(pp);
1585+
get_page(pp);
1586+
}
15791587
unlock_page(pp);
15801588
put_page(pp);
15811589
}

module/os/linux/zfs/zpl_file.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,42 @@ zpl_writepage(struct page *pp, struct writeback_control *wbc)
607607
return (zpl_putpage(pp, wbc, &for_sync));
608608
}
609609

610+
static int
611+
zpl_releasepage(struct page *pp, gfp_t gfp)
612+
{
613+
if (PagePrivate(pp)) {
614+
ClearPagePrivate(pp);
615+
put_page(pp);
616+
}
617+
return (1);
618+
}
619+
620+
#ifdef HAVE_VFS_RELEASE_FOLIO
621+
static bool
622+
zpl_release_folio(struct folio *folio, gfp_t gfp)
623+
{
624+
return (zpl_releasepage(&folio->page, gfp));
625+
}
626+
#endif
627+
628+
#ifdef HAVE_VFS_INVALIDATE_FOLIO
629+
static void
630+
zpl_invalidate_folio(struct folio *folio, size_t offset, size_t len)
631+
{
632+
if ((offset == 0) && (len == PAGE_SIZE)) {
633+
zpl_releasepage(&folio->page, 0);
634+
}
635+
}
636+
#else
637+
static void
638+
zpl_invalidatepage(struct page *pp, unsigned int offset, unsigned int len)
639+
{
640+
if ((offset == 0) && (len == PAGE_SIZE)) {
641+
zpl_releasepage(pp, 0);
642+
}
643+
}
644+
#endif
645+
610646
/*
611647
* The flag combination which matches the behavior of zfs_space() is
612648
* FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE. The FALLOC_FL_PUNCH_HOLE
@@ -1090,6 +1126,16 @@ const struct address_space_operations zpl_address_space_operations = {
10901126
#ifdef HAVE_VFS_FILEMAP_DIRTY_FOLIO
10911127
.dirty_folio = filemap_dirty_folio,
10921128
#endif
1129+
#ifdef HAVE_VFS_RELEASE_FOLIO
1130+
.release_folio = zpl_release_folio,
1131+
#else
1132+
.releasepage = zpl_releasepage,
1133+
#endif
1134+
#ifdef HAVE_VFS_INVALIDATE_FOLIO
1135+
.invalidate_folio = zpl_invalidate_folio,
1136+
#else
1137+
.invalidatepage = zpl_invalidatepage,
1138+
#endif
10931139
};
10941140

10951141
const struct file_operations zpl_file_operations = {

0 commit comments

Comments
 (0)