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