git: 55038a6306a5 - main - LinuxKPI: add simplified vesion of page_frag_cache

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Sat, 31 Dec 2022 02:47:56 UTC
The branch main has been updated by bz:

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

commit 55038a6306a570c9f2df89f5ad076de0f7d98152
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2022-12-03 00:33:34 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2022-12-31 02:45:44 +0000

    LinuxKPI: add simplified vesion of page_frag_cache
    
    For the moment and the currently only consumer (mt76) add a simplified
    version of the page_frag_cache.  We will only accept fragement sizes up
    to 1 PAGE_SIZE (KASSERT) and we will always return a full page.
    Should we add more consumers or small (or large) objects would become a
    problem we can always add a more elaborate version.
    
    Discussed with: markj
    Reviewed by:    markj (,hselasky commented as well)
    MFC after:      3 days
    Differential Revision: https://reviews.freebsd.org/D37595
---
 sys/compat/linuxkpi/common/include/linux/gfp.h | 29 +++++++++++++++++
 sys/compat/linuxkpi/common/src/linux_page.c    | 45 ++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h
index 63b23b4b637e..a2930b44b0c4 100644
--- a/sys/compat/linuxkpi/common/include/linux/gfp.h
+++ b/sys/compat/linuxkpi/common/include/linux/gfp.h
@@ -81,6 +81,11 @@
 CTASSERT((__GFP_DMA32 & GFP_NATIVE_MASK) == 0);
 CTASSERT((__GFP_BITS_MASK & GFP_NATIVE_MASK) == GFP_NATIVE_MASK);
 
+struct page_frag_cache {
+	void *va;
+	int pagecnt_bias;
+};
+
 /*
  * Resolve a page into a virtual address:
  *
@@ -95,6 +100,9 @@ extern void *linux_page_address(struct page *);
  */
 extern vm_page_t linux_alloc_pages(gfp_t flags, unsigned int order);
 extern void linux_free_pages(vm_page_t page, unsigned int order);
+void *linuxkpi_page_frag_alloc(struct page_frag_cache *, size_t, gfp_t);
+void linuxkpi_page_frag_free(void *);
+void linuxkpi__page_frag_cache_drain(struct page *, size_t);
 
 static inline struct page *
 alloc_page(gfp_t flags)
@@ -176,6 +184,27 @@ free_page(uintptr_t addr)
 	linux_free_kmem(addr, 0);
 }
 
+static inline void *
+page_frag_alloc(struct page_frag_cache *pfc, size_t fragsz, gfp_t gfp)
+{
+
+	return (linuxkpi_page_frag_alloc(pfc, fragsz, gfp));
+}
+
+static inline void
+page_frag_free(void *addr)
+{
+
+	linuxkpi_page_frag_free(addr);
+}
+
+static inline void
+__page_frag_cache_drain(struct page *page, size_t count)
+{
+
+	linuxkpi__page_frag_cache_drain(page, count);
+}
+
 static inline bool
 gfpflags_allow_blocking(const gfp_t gfp_flags)
 {
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 26b0ed649372..2bbe8f502cba 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -429,3 +429,48 @@ lkpi_arch_phys_wc_del(int reg)
 	free(mrdesc, M_LKMTRR);
 #endif
 }
+
+/*
+ * This is a highly simplified version of the Linux page_frag_cache.
+ * We only support up-to 1 single page as fragment size and we will
+ * always return a full page.  This may be wasteful on small objects
+ * but the only known consumer (mt76) is either asking for a half-page
+ * or a full page.  If this was to become a problem we can implement
+ * a more elaborate version.
+ */
+void *
+linuxkpi_page_frag_alloc(struct page_frag_cache *pfc,
+    size_t fragsz, gfp_t gfp)
+{
+	vm_page_t pages;
+
+	if (fragsz == 0)
+		return (NULL);
+
+	KASSERT(fragsz <= PAGE_SIZE, ("%s: fragsz %zu > PAGE_SIZE not yet "
+	    "supported", __func__, fragsz));
+
+	pages = alloc_pages(gfp, flsl(howmany(fragsz, PAGE_SIZE) - 1));
+	pfc->va = linux_page_address(pages);
+
+	/* Passed in as "count" to __page_frag_cache_drain(). Unused by us. */
+	pfc->pagecnt_bias = 0;
+
+	return (pfc->va);
+}
+
+void
+linuxkpi_page_frag_free(void *addr)
+{
+	vm_page_t page;
+
+	page = PHYS_TO_VM_PAGE(vtophys(addr));
+	linux_free_pages(page, 0);
+}
+
+void
+linuxkpi__page_frag_cache_drain(struct page *page, size_t count __unused)
+{
+
+	linux_free_pages(page, 0);
+}