Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 May 2025 17:46:52 GMT
From:      =?utf-8?Q?Jean-S=C3=A9bastien?= =?utf-8?Q?P=C3=A9dron?= <dumbbell@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: e3b16f53a645 - main - linuxkpi: Add `folio` and `folio_batch` APIs
Message-ID:  <202505121746.54CHkqK7035413@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by dumbbell:

URL: https://cgit.FreeBSD.org/src/commit/?id=e3b16f53a6455903a7e814045584fe203d4fff64

commit e3b16f53a6455903a7e814045584fe203d4fff64
Author:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2025-04-13 11:23:01 +0000
Commit:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2025-05-12 17:08:03 +0000

    linuxkpi: Add `folio` and `folio_batch` APIs
    
    They are used by the i915 DRM driver in Linux 6.6 (although this change
    was only backported with Linux 6.7 DRM drivers).
    
    `struct folio` simply wraps `struct page` for now.
    
    `struct folio_batch` is the same as `struct pagevec` but it works with
    `struct folio` instead of `struct page` directly.
    
    Reviewed by:    bz, kib, markj
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D48743
---
 sys/compat/linuxkpi/common/include/linux/gfp.h     |  2 +
 sys/compat/linuxkpi/common/include/linux/mm.h      | 64 +++++++++++++++++++-
 .../linuxkpi/common/include/linux/mm_types.h       | 11 ++++
 .../linuxkpi/common/include/linux/page-flags.h     |  7 +++
 sys/compat/linuxkpi/common/include/linux/pagemap.h |  2 +
 sys/compat/linuxkpi/common/include/linux/pagevec.h | 68 ++++++++++++++++++++++
 .../linuxkpi/common/include/linux/scatterlist.h    |  7 +++
 .../linuxkpi/common/include/linux/shmem_fs.h       | 10 ++++
 sys/compat/linuxkpi/common/include/linux/swap.h    | 14 +++++
 sys/compat/linuxkpi/common/src/linux_folio.c       | 58 ++++++++++++++++++
 sys/compat/linuxkpi/common/src/linux_page.c        |  6 +-
 sys/conf/files                                     |  2 +
 sys/modules/linuxkpi/Makefile                      |  1 +
 13 files changed, 248 insertions(+), 4 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h
index 35dbe3e2a436..4c4caa621789 100644
--- a/sys/compat/linuxkpi/common/include/linux/gfp.h
+++ b/sys/compat/linuxkpi/common/include/linux/gfp.h
@@ -134,6 +134,8 @@ dev_alloc_pages(unsigned int order)
 	return (linux_alloc_pages(GFP_ATOMIC, order));
 }
 
+struct folio *folio_alloc(gfp_t gfp, unsigned int order);
+
 /*
  * Page management for mapped pages:
  */
diff --git a/sys/compat/linuxkpi/common/include/linux/mm.h b/sys/compat/linuxkpi/common/include/linux/mm.h
index 569e2a8b70f1..068dbb627c82 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm.h
@@ -161,6 +161,14 @@ virt_to_head_page(const void *p)
 	return (virt_to_page(p));
 }
 
+static inline struct folio *
+virt_to_folio(const void *p)
+{
+	struct page *page = virt_to_page(p);
+
+	return (page_folio(page));
+}
+
 /*
  * Compute log2 of the power of two rounded up count of pages
  * needed for size bytes.
@@ -282,8 +290,30 @@ put_page(struct page *page)
 	__free_page(page);
 }
 
-void linux_release_pages(struct page **pages, int nr);
-#define	release_pages(pages, nr) linux_release_pages((pages), (nr))
+static inline void
+folio_get(struct folio *folio)
+{
+	get_page(&folio->page);
+}
+
+static inline void
+folio_put(struct folio *folio)
+{
+	put_page(&folio->page);
+}
+
+/*
+ * Linux uses the following "transparent" union so that `release_pages()`
+ * accepts both a list of `struct page` or a list of `struct folio`. This
+ * relies on the fact that a `struct folio` can be cast to a `struct page`.
+ */
+typedef union {
+	struct page **pages;
+	struct folio **folios;
+} release_pages_arg __attribute__ ((__transparent_union__));
+
+void linux_release_pages(release_pages_arg arg, int nr);
+#define	release_pages(arg, nr) linux_release_pages((arg), (nr))
 
 extern long
 lkpi_get_user_pages(unsigned long start, unsigned long nr_pages,
@@ -416,4 +446,34 @@ want_init_on_free(void)
 	return (false);
 }
 
+static inline unsigned long
+folio_pfn(struct folio *folio)
+{
+	return (page_to_pfn(&folio->page));
+}
+
+static inline long
+folio_nr_pages(struct folio *folio)
+{
+	return (1);
+}
+
+static inline size_t
+folio_size(struct folio *folio)
+{
+	return (PAGE_SIZE);
+}
+
+static inline void
+folio_mark_dirty(struct folio *folio)
+{
+	set_page_dirty(&folio->page);
+}
+
+static inline void *
+folio_address(const struct folio *folio)
+{
+	return (page_address(&folio->page));
+}
+
 #endif					/* _LINUXKPI_LINUX_MM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mm_types.h b/sys/compat/linuxkpi/common/include/linux/mm_types.h
index c08e2511725b..3ea68e97004c 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm_types.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm_types.h
@@ -79,4 +79,15 @@ mmgrab(struct mm_struct *mm)
 extern struct mm_struct *linux_get_task_mm(struct task_struct *);
 #define	get_task_mm(task) linux_get_task_mm(task)
 
+struct folio {
+	/*
+	 * The page member must be at the beginning because `page_folio(p)`
+	 * casts from a `struct page` to a `struct folio`.
+	 *
+	 * `release_pages()` also relies on this to be able to accept either a
+	 * list of `struct page` or a list of `struct folio`.
+	 */
+	struct page page;
+};
+
 #endif					/* _LINUXKPI_LINUX_MM_TYPES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/page-flags.h b/sys/compat/linuxkpi/common/include/linux/page-flags.h
index 9dd49c8492a5..a22b3a24c330 100644
--- a/sys/compat/linuxkpi/common/include/linux/page-flags.h
+++ b/sys/compat/linuxkpi/common/include/linux/page-flags.h
@@ -29,6 +29,13 @@
 #ifndef _LINUXKPI_LINUX_PAGEFLAGS_H_
 #define _LINUXKPI_LINUX_PAGEFLAGS_H_
 
+#include <linux/mm_types.h>
+
 #define	PageHighMem(p)		(0)
 
+#define	page_folio(p) \
+	(_Generic((p), \
+	      const struct page *: (const struct folio *)(p), \
+	      struct page *: (struct folio *)(p)))
+
 #endif	/* _LINUXKPI_LINUX_PAGEFLAGS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pagemap.h b/sys/compat/linuxkpi/common/include/linux/pagemap.h
index 92159f7a2eb8..cb6a1820ea8b 100644
--- a/sys/compat/linuxkpi/common/include/linux/pagemap.h
+++ b/sys/compat/linuxkpi/common/include/linux/pagemap.h
@@ -33,6 +33,8 @@
 #include <linux/highmem.h>
 #include <linux/vmalloc.h>
 
+struct folio_batch;
+
 #define	invalidate_mapping_pages(...) \
   linux_invalidate_mapping_pages(__VA_ARGS__)
 
diff --git a/sys/compat/linuxkpi/common/include/linux/pagevec.h b/sys/compat/linuxkpi/common/include/linux/pagevec.h
index 9ba8ff8effa0..0a952e965b5a 100644
--- a/sys/compat/linuxkpi/common/include/linux/pagevec.h
+++ b/sys/compat/linuxkpi/common/include/linux/pagevec.h
@@ -66,4 +66,72 @@ check_move_unevictable_pages(struct pagevec *pvec)
 {
 }
 
+/*
+ * struct folio
+ *
+ * On Linux, `struct folio` replaces `struct page`. To manage a list of folios,
+ * there is `struct folio_batch` on top of this, which replaces `struct
+ * pagevec` above.
+ *
+ * Here is the original description when `struct folio` was added to the Linux
+ * kernel:
+ *   "A struct folio is a new abstraction to replace the venerable struct page.
+ *   A function which takes a struct folio argument declares that it will
+ *   operate on the entire (possibly compound) page, not just PAGE_SIZE bytes.
+ *   In return, the caller guarantees that the pointer it is passing does not
+ *   point to a tail page.  No change to generated code."
+ */
+
+struct folio;
+
+struct folio_batch {
+	uint8_t	nr;
+	struct folio *folios[PAGEVEC_SIZE];
+};
+
+static inline void
+folio_batch_init(struct folio_batch *fbatch)
+{
+	fbatch->nr = 0;
+}
+
+static inline void
+folio_batch_reinit(struct folio_batch *fbatch)
+{
+	fbatch->nr = 0;
+}
+
+static inline unsigned int
+folio_batch_count(struct folio_batch *fbatch)
+{
+	return (fbatch->nr);
+}
+
+static inline unsigned int
+folio_batch_space(struct folio_batch *fbatch)
+{
+	return (PAGEVEC_SIZE - fbatch->nr);
+}
+
+static inline unsigned int
+folio_batch_add(struct folio_batch *fbatch, struct folio *folio)
+{
+	KASSERT(
+	    fbatch->nr < PAGEVEC_SIZE,
+	    ("struct folio_batch %p is full", fbatch));
+
+	fbatch->folios[fbatch->nr++] = folio;
+
+	return (folio_batch_space(fbatch));
+}
+
+void __folio_batch_release(struct folio_batch *fbatch);
+
+static inline void
+folio_batch_release(struct folio_batch *fbatch)
+{
+	if (folio_batch_count(fbatch))
+		__folio_batch_release(fbatch);
+}
+
 #endif	/* _LINUXKPI_LINUX_PAGEVEC_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
index 51ced19e6b5b..537f5bebc5aa 100644
--- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h
+++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
@@ -674,4 +674,11 @@ sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
 	return (total);
 }
 
+static inline void
+sg_set_folio(struct scatterlist *sg, struct folio *folio, size_t len,
+    size_t offset)
+{
+	sg_set_page(sg, &folio->page, len, offset);
+}
+
 #endif					/* _LINUXKPI_LINUX_SCATTERLIST_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/shmem_fs.h b/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
index efa2c855fe7d..5e91725d4a1c 100644
--- a/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
@@ -54,4 +54,14 @@ void linux_shmem_truncate_range(vm_object_t obj, loff_t lstart,
 #define	shmem_truncate_range(...) \
   linux_shmem_truncate_range(__VA_ARGS__)
 
+static inline struct folio *
+shmem_read_folio_gfp(vm_object_t obj, int pindex, gfp_t gfp)
+{
+	struct page *page;
+
+	page = shmem_read_mapping_page_gfp(obj, pindex, gfp);
+
+	return (page_folio(page));
+}
+
 #endif /* _LINUXKPI_LINUX_SHMEM_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/swap.h b/sys/compat/linuxkpi/common/include/linux/swap.h
index a353d353cd33..5828db7ae392 100644
--- a/sys/compat/linuxkpi/common/include/linux/swap.h
+++ b/sys/compat/linuxkpi/common/include/linux/swap.h
@@ -37,6 +37,9 @@
 #include <vm/swap_pager.h>
 #include <vm/vm_pageout.h>
 
+#include <linux/pagemap.h>
+#include <linux/page-flags.h>
+
 static inline long
 get_nr_swap_pages(void)
 {
@@ -54,4 +57,15 @@ current_is_kswapd(void)
 	return (curproc == pageproc);
 }
 
+static inline void
+folio_mark_accessed(struct folio *folio)
+{
+	mark_page_accessed(&folio->page);
+}
+
+static inline void
+check_move_unevictable_folios(struct folio_batch *fbatch)
+{
+}
+
 #endif
diff --git a/sys/compat/linuxkpi/common/src/linux_folio.c b/sys/compat/linuxkpi/common/src/linux_folio.c
new file mode 100644
index 000000000000..c2af7792be04
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_folio.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ * Copyright (c) 2024-2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/page.h>
+#include <linux/pagevec.h>
+
+struct folio *
+folio_alloc(gfp_t gfp, unsigned int order)
+{
+	struct page *page;
+	struct folio *folio;
+
+	/*
+	 * Allocated pages are wired already. There is no need to increase a
+	 * refcount here.
+	 */
+	page = alloc_pages(gfp | __GFP_COMP, order);
+	folio = (struct folio *)page;
+
+	return (folio);
+}
+
+void
+__folio_batch_release(struct folio_batch *fbatch)
+{
+	release_pages(fbatch->folios, folio_batch_count(fbatch));
+
+	folio_batch_reinit(fbatch);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 7ce52cbf95eb..ebb92eacbf9a 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -194,12 +194,14 @@ linux_free_pages(struct page *page, unsigned int order)
 }
 
 void
-linux_release_pages(struct page **pages, int nr)
+linux_release_pages(release_pages_arg arg, int nr)
 {
 	int i;
 
+	CTASSERT(offsetof(struct folio, page) == 0);
+
 	for (i = 0; i < nr; i++)
-		__free_page(pages[i]);
+		__free_page(arg.pages[i]);
 }
 
 vm_offset_t
diff --git a/sys/conf/files b/sys/conf/files
index 66d96acedfc6..6ad3ea21b14e 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4595,6 +4595,8 @@ compat/linuxkpi/common/src/linux_domain.c	optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_firmware.c	optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/linux_folio.c	optional compat_linuxkpi \
+	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_fpu.c		optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_hrtimer.c	optional compat_linuxkpi \
diff --git a/sys/modules/linuxkpi/Makefile b/sys/modules/linuxkpi/Makefile
index 692f69c1f4e1..a662f5dffbb6 100644
--- a/sys/modules/linuxkpi/Makefile
+++ b/sys/modules/linuxkpi/Makefile
@@ -7,6 +7,7 @@ SRCS=	linux_compat.c \
 	linux_dmi.c \
 	linux_domain.c \
 	linux_firmware.c \
+	linux_folio.c \
 	linux_fpu.c \
 	linux_hrtimer.c \
 	linux_idr.c \



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202505121746.54CHkqK7035413>