1*e591a766SBjoern A. Zeeb /*
2*e591a766SBjoern A. Zeeb * Copyright (c) 2025-2026 The FreeBSD Foundation
3*e591a766SBjoern A. Zeeb *
4*e591a766SBjoern A. Zeeb * This software was developed by Björn Zeeb under sponsorship from
5*e591a766SBjoern A. Zeeb * the FreeBSD Foundation.
6*e591a766SBjoern A. Zeeb *
7*e591a766SBjoern A. Zeeb * SPDX-License-Identifier: BSD-2-Clause
8*e591a766SBjoern A. Zeeb */
9*e591a766SBjoern A. Zeeb
10*e591a766SBjoern A. Zeeb #include <linux/types.h>
11*e591a766SBjoern A. Zeeb #include <linux/dma-mapping.h>
12*e591a766SBjoern A. Zeeb #include <net/page_pool/helpers.h>
13*e591a766SBjoern A. Zeeb
14*e591a766SBjoern A. Zeeb struct page_pool *
linuxkpi_page_pool_create(const struct page_pool_params * ppparams)15*e591a766SBjoern A. Zeeb linuxkpi_page_pool_create(const struct page_pool_params *ppparams)
16*e591a766SBjoern A. Zeeb {
17*e591a766SBjoern A. Zeeb struct page_pool *pp;
18*e591a766SBjoern A. Zeeb int error;
19*e591a766SBjoern A. Zeeb
20*e591a766SBjoern A. Zeeb #ifndef PAGE_IS_LKPI_PAGE
21*e591a766SBjoern A. Zeeb WARN_ONCE(1, "\n!!! LinuxKPI page pool not supported yet!!!\n\n");
22*e591a766SBjoern A. Zeeb #endif
23*e591a766SBjoern A. Zeeb
24*e591a766SBjoern A. Zeeb pp = kzalloc_node(sizeof(*pp), GFP_KERNEL, ppparams->nid);
25*e591a766SBjoern A. Zeeb if (pp == NULL)
26*e591a766SBjoern A. Zeeb return (ERR_PTR(-ENOMEM));
27*e591a766SBjoern A. Zeeb
28*e591a766SBjoern A. Zeeb pp->params = *ppparams;
29*e591a766SBjoern A. Zeeb
30*e591a766SBjoern A. Zeeb if ((pp->params.flags & PP_FLAG_DMA_MAP) != 0) {
31*e591a766SBjoern A. Zeeb if (pp->params.dma_dir != DMA_FROM_DEVICE &&
32*e591a766SBjoern A. Zeeb pp->params.dma_dir != DMA_BIDIRECTIONAL) {
33*e591a766SBjoern A. Zeeb pr_warn_once("%s: invalid dma_dir %#04x\n",
34*e591a766SBjoern A. Zeeb __func__, pp->params.dma_dir);
35*e591a766SBjoern A. Zeeb error = -EINVAL;
36*e591a766SBjoern A. Zeeb goto free_pp;
37*e591a766SBjoern A. Zeeb }
38*e591a766SBjoern A. Zeeb }
39*e591a766SBjoern A. Zeeb
40*e591a766SBjoern A. Zeeb if ((pp->params.flags & PP_FLAG_DMA_SYNC_DEV) != 0) {
41*e591a766SBjoern A. Zeeb if ((pp->params.flags & PP_FLAG_DMA_MAP) == 0) {
42*e591a766SBjoern A. Zeeb pr_warn_once("%s: DMA_SYNC_DEV but no DMA_MAP\n",
43*e591a766SBjoern A. Zeeb __func__);
44*e591a766SBjoern A. Zeeb error = -EINVAL;
45*e591a766SBjoern A. Zeeb goto free_pp;
46*e591a766SBjoern A. Zeeb }
47*e591a766SBjoern A. Zeeb
48*e591a766SBjoern A. Zeeb if (pp->params.max_len == 0) {
49*e591a766SBjoern A. Zeeb pr_warn_once("%s: max_len unset\n", __func__);
50*e591a766SBjoern A. Zeeb error = -EINVAL;
51*e591a766SBjoern A. Zeeb goto free_pp;
52*e591a766SBjoern A. Zeeb }
53*e591a766SBjoern A. Zeeb }
54*e591a766SBjoern A. Zeeb
55*e591a766SBjoern A. Zeeb return (pp);
56*e591a766SBjoern A. Zeeb
57*e591a766SBjoern A. Zeeb free_pp:
58*e591a766SBjoern A. Zeeb kfree(pp);
59*e591a766SBjoern A. Zeeb return (ERR_PTR(error));
60*e591a766SBjoern A. Zeeb }
61*e591a766SBjoern A. Zeeb
62*e591a766SBjoern A. Zeeb void
linuxkpi_page_pool_destroy(struct page_pool * pp)63*e591a766SBjoern A. Zeeb linuxkpi_page_pool_destroy(struct page_pool *pp)
64*e591a766SBjoern A. Zeeb {
65*e591a766SBjoern A. Zeeb kfree(pp);
66*e591a766SBjoern A. Zeeb }
67*e591a766SBjoern A. Zeeb
68*e591a766SBjoern A. Zeeb /* ---------------------------------------------------------------------------- */
69*e591a766SBjoern A. Zeeb
70*e591a766SBjoern A. Zeeb struct page *
linuxkpi_page_pool_alloc_frag(struct page_pool * pp,uint32_t * offset,size_t size,gfp_t gfp)71*e591a766SBjoern A. Zeeb linuxkpi_page_pool_alloc_frag(struct page_pool *pp, uint32_t *offset,
72*e591a766SBjoern A. Zeeb size_t size, gfp_t gfp)
73*e591a766SBjoern A. Zeeb {
74*e591a766SBjoern A. Zeeb #ifdef PAGE_IS_LKPI_PAGE
75*e591a766SBjoern A. Zeeb struct page *p;
76*e591a766SBjoern A. Zeeb dma_addr_t dma;
77*e591a766SBjoern A. Zeeb
78*e591a766SBjoern A. Zeeb if (size > PAGE_SIZE) {
79*e591a766SBjoern A. Zeeb pr_warn_once("%s: size %zu > PAGE_SIZE\n", __func__, size);
80*e591a766SBjoern A. Zeeb return (NULL);
81*e591a766SBjoern A. Zeeb }
82*e591a766SBjoern A. Zeeb
83*e591a766SBjoern A. Zeeb /*
84*e591a766SBjoern A. Zeeb * We do not try to be smart on the first cut until 'struct page' work
85*e591a766SBjoern A. Zeeb * is sorted and settled. Everyone gets a page for now.
86*e591a766SBjoern A. Zeeb * XXX in the future deal with page fragments, pool limits, etc.
87*e591a766SBjoern A. Zeeb */
88*e591a766SBjoern A. Zeeb p = alloc_page(gfp);
89*e591a766SBjoern A. Zeeb if (p == NULL)
90*e591a766SBjoern A. Zeeb return (NULL);
91*e591a766SBjoern A. Zeeb
92*e591a766SBjoern A. Zeeb if (offset != NULL)
93*e591a766SBjoern A. Zeeb *offset = 0;
94*e591a766SBjoern A. Zeeb
95*e591a766SBjoern A. Zeeb if ((pp->params.flags & PP_FLAG_DMA_MAP) != 0) {
96*e591a766SBjoern A. Zeeb dma = dma_map_page_attrs(pp->params.dev, p, 0, PAGE_SIZE,
97*e591a766SBjoern A. Zeeb pp->params.dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
98*e591a766SBjoern A. Zeeb if (dma_mapping_error(pp->params.dev, dma)) {
99*e591a766SBjoern A. Zeeb put_page(p);
100*e591a766SBjoern A. Zeeb return (NULL);
101*e591a766SBjoern A. Zeeb }
102*e591a766SBjoern A. Zeeb
103*e591a766SBjoern A. Zeeb /* We store the dma address in struct page as well. */
104*e591a766SBjoern A. Zeeb p->dma_addr = dma;
105*e591a766SBjoern A. Zeeb }
106*e591a766SBjoern A. Zeeb
107*e591a766SBjoern A. Zeeb if ((pp->params.flags & PP_FLAG_DMA_SYNC_DEV) != 0)
108*e591a766SBjoern A. Zeeb dma_sync_single_for_device(pp->params.dev, dma, PAGE_SIZE,
109*e591a766SBjoern A. Zeeb pp->params.dma_dir);
110*e591a766SBjoern A. Zeeb
111*e591a766SBjoern A. Zeeb /*
112*e591a766SBjoern A. Zeeb * We may not yet need to track the page pool on the page in order
113*e591a766SBjoern A. Zeeb * to know where to return it but prepare at least for that.
114*e591a766SBjoern A. Zeeb */
115*e591a766SBjoern A. Zeeb p->pp = pp;
116*e591a766SBjoern A. Zeeb
117*e591a766SBjoern A. Zeeb return (p);
118*e591a766SBjoern A. Zeeb #else
119*e591a766SBjoern A. Zeeb return (NULL);
120*e591a766SBjoern A. Zeeb #endif /* PAGE_IS_LKPI_PAGE */
121*e591a766SBjoern A. Zeeb }
122*e591a766SBjoern A. Zeeb
123*e591a766SBjoern A. Zeeb struct page *
linuxkpi_page_pool_dev_alloc_frag(struct page_pool * pp,uint32_t * offset,size_t size)124*e591a766SBjoern A. Zeeb linuxkpi_page_pool_dev_alloc_frag(struct page_pool *pp, uint32_t *offset,
125*e591a766SBjoern A. Zeeb size_t size)
126*e591a766SBjoern A. Zeeb {
127*e591a766SBjoern A. Zeeb gfp_t gfp;
128*e591a766SBjoern A. Zeeb
129*e591a766SBjoern A. Zeeb gfp = (GFP_ATOMIC | __GFP_NOWARN);
130*e591a766SBjoern A. Zeeb return (page_pool_alloc_frag(pp, offset, size, gfp));
131*e591a766SBjoern A. Zeeb }
132*e591a766SBjoern A. Zeeb
133*e591a766SBjoern A. Zeeb void
linuxkpi_page_pool_put_full_page(struct page_pool * pp,struct page * page,bool allow_direct)134*e591a766SBjoern A. Zeeb linuxkpi_page_pool_put_full_page(struct page_pool *pp, struct page *page,
135*e591a766SBjoern A. Zeeb bool allow_direct)
136*e591a766SBjoern A. Zeeb {
137*e591a766SBjoern A. Zeeb /* We just free the full page we handed out for now. */
138*e591a766SBjoern A. Zeeb #ifdef PAGE_IS_LKPI_PAGE
139*e591a766SBjoern A. Zeeb page->pp = NULL;
140*e591a766SBjoern A. Zeeb page->dma_addr = 0;
141*e591a766SBjoern A. Zeeb #endif
142*e591a766SBjoern A. Zeeb put_page(page);
143*e591a766SBjoern A. Zeeb }
144*e591a766SBjoern A. Zeeb
145*e591a766SBjoern A. Zeeb dma_addr_t
linuxkpi_page_pool_get_dma_addr(const struct page * page)146*e591a766SBjoern A. Zeeb linuxkpi_page_pool_get_dma_addr(const struct page *page)
147*e591a766SBjoern A. Zeeb {
148*e591a766SBjoern A. Zeeb
149*e591a766SBjoern A. Zeeb KASSERT(page != NULL, ("%s: page %p\n", __func__, page));
150*e591a766SBjoern A. Zeeb
151*e591a766SBjoern A. Zeeb #ifdef PAGE_IS_LKPI_PAGE
152*e591a766SBjoern A. Zeeb return (page->dma_addr);
153*e591a766SBjoern A. Zeeb #else
154*e591a766SBjoern A. Zeeb return (0);
155*e591a766SBjoern A. Zeeb #endif
156*e591a766SBjoern A. Zeeb }
157