From becc717f6143caf80bad6879b6c6402538ecae89 Mon Sep 17 00:00:00 2001 From: Brian Atkinson Date: Mon, 14 Mar 2022 13:37:39 -0600 Subject: [PATCH] Adding ZERO_PAGE detection On some architectures ZERO_PAGE is unavailable because it references a GPL exported symbol of empty_zero_page. Originally e08b993 removed the call to PAGE_ZERO(0) for assignment to the abd_zero_page. However, a simple check can be done to avoid a kernel allocation and free for the abd_zero_page if ZERO_PAGE is available. Reviewed-by: Brian Behlendorf Signed-off-by: Brian Atkinson Closes #13199 --- config/kernel-zero_page.m4 | 27 +++++++++++++++++++++++++++ config/kernel.m4 | 2 ++ module/os/linux/zfs/abd_os.c | 15 ++++++++++++--- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 config/kernel-zero_page.m4 diff --git a/config/kernel-zero_page.m4 b/config/kernel-zero_page.m4 new file mode 100644 index 000000000..1461781ac --- /dev/null +++ b/config/kernel-zero_page.m4 @@ -0,0 +1,27 @@ +dnl # +dnl # ZERO_PAGE() is an alias for emtpy_zero_page. On certain architectures +dnl # this is a GPL exported variable. +dnl # + +dnl # +dnl # Checking if ZERO_PAGE is exported GPL-only +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_ZERO_PAGE], [ + ZFS_LINUX_TEST_SRC([zero_page], [ + #include + ], [ + struct page *p __attribute__ ((unused)); + p = ZERO_PAGE(0); + ], [], [ZFS_META_LICENSE]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_ZERO_PAGE], [ + AC_MSG_CHECKING([whether ZERO_PAGE() is GPL-only]) + ZFS_LINUX_TEST_RESULT([zero_page_license], [ + AC_MSG_RESULT(no) + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ZERO_PAGE_GPL_ONLY, 1, + [ZERO_PAGE() is GPL-only]) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index 3122e9dba..d1d3dede1 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -137,6 +137,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_PAGEMAP_FOLIO_WAIT_BIT ZFS_AC_KERNEL_SRC_ADD_DISK ZFS_AC_KERNEL_SRC_KTHREAD + ZFS_AC_KERNEL_SRC_ZERO_PAGE AC_MSG_CHECKING([for available kernel interfaces]) ZFS_LINUX_TEST_COMPILE_ALL([kabi]) @@ -247,6 +248,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_PAGEMAP_FOLIO_WAIT_BIT ZFS_AC_KERNEL_ADD_DISK ZFS_AC_KERNEL_KTHREAD + ZFS_AC_KERNEL_ZERO_PAGE ]) dnl # diff --git a/module/os/linux/zfs/abd_os.c b/module/os/linux/zfs/abd_os.c index 113aee585..688458621 100644 --- a/module/os/linux/zfs/abd_os.c +++ b/module/os/linux/zfs/abd_os.c @@ -182,8 +182,11 @@ abd_t *abd_zero_scatter = NULL; struct page; /* - * abd_zero_page we will be an allocated zero'd PAGESIZE buffer, which is - * assigned to set each of the pages of abd_zero_scatter. + * _KERNEL - Will point to ZERO_PAGE if it is available or it will be + * an allocated zero'd PAGESIZE buffer. + * Userspace - Will be an allocated zero'ed PAGESIZE buffer. + * + * abd_zero_page is assigned to each of the pages of abd_zero_scatter. */ static struct page *abd_zero_page = NULL; @@ -466,15 +469,19 @@ abd_alloc_zero_scatter(void) struct scatterlist *sg = NULL; struct sg_table table; gfp_t gfp = __GFP_NOWARN | GFP_NOIO; - gfp_t gfp_zero_page = gfp | __GFP_ZERO; int nr_pages = abd_chunkcnt_for_bytes(SPA_MAXBLOCKSIZE); int i = 0; +#if defined(HAVE_ZERO_PAGE_GPL_ONLY) + gfp_t gfp_zero_page = gfp | __GFP_ZERO; while ((abd_zero_page = __page_cache_alloc(gfp_zero_page)) == NULL) { ABDSTAT_BUMP(abdstat_scatter_page_alloc_retry); schedule_timeout_interruptible(1); } abd_mark_zfs_page(abd_zero_page); +#else + abd_zero_page = ZERO_PAGE(0); +#endif /* HAVE_ZERO_PAGE_GPL_ONLY */ while (sg_alloc_table(&table, nr_pages, gfp)) { ABDSTAT_BUMP(abdstat_scatter_sg_table_retry); @@ -695,8 +702,10 @@ abd_free_zero_scatter(void) abd_zero_scatter = NULL; ASSERT3P(abd_zero_page, !=, NULL); #if defined(_KERNEL) +#if defined(HAVE_ZERO_PAGE_GPL_ONLY) abd_unmark_zfs_page(abd_zero_page); __free_page(abd_zero_page); +#endif /* HAVE_ZERO_PAGE_GPL_ONLY */ #else umem_free(abd_zero_page, PAGESIZE); #endif /* _KERNEL */