git: 9bf3a5616c51 - stable/13 - LinuxKPI: add simplified version of page_frag_cache
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 18 Jan 2023 16:24:52 UTC
The branch stable/13 has been updated by bz:
URL: https://cgit.FreeBSD.org/src/commit/?id=9bf3a5616c51081b221cbb794f559a5aab07aabc
commit 9bf3a5616c51081b221cbb794f559a5aab07aabc
Author: Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2022-12-03 00:33:34 +0000
Commit: Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2023-01-18 13:25:11 +0000
LinuxKPI: add simplified version 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
(cherry picked from commit 55038a6306a570c9f2df89f5ad076de0f7d98152)
LinuxKPI: fix possible NULL dereference in linuxkpi_page_frag_alloc()
Reported by: Coverity via emaste
Coverity ID: 1502345
Sponsored by: The FreeBSD Foundation
(cherry picked from commit 51e94a4658d23016dd0ae67c189a89f53281cbcd)
---
sys/compat/linuxkpi/common/include/linux/gfp.h | 29 ++++++++++++++++
sys/compat/linuxkpi/common/src/linux_page.c | 47 ++++++++++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h
index 6273fa969db8..7657e78b0950 100644
--- a/sys/compat/linuxkpi/common/include/linux/gfp.h
+++ b/sys/compat/linuxkpi/common/include/linux/gfp.h
@@ -80,6 +80,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:
*
@@ -94,6 +99,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)
@@ -175,6 +183,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 df4a124cf3e2..5fa370147045 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -429,3 +429,50 @@ 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));
+ if (pages == NULL)
+ return (NULL);
+ 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);
+}