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 *
linuxkpi_page_pool_create(const struct page_pool_params * ppparams)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
linuxkpi_page_pool_destroy(struct page_pool * pp)63 linuxkpi_page_pool_destroy(struct page_pool *pp)
64 {
65 kfree(pp);
66 }
67
68 /* ---------------------------------------------------------------------------- */
69
70 struct page *
linuxkpi_page_pool_alloc_frag(struct page_pool * pp,uint32_t * offset,size_t size,gfp_t gfp)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 *
linuxkpi_page_pool_dev_alloc_frag(struct page_pool * pp,uint32_t * offset,size_t size)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
linuxkpi_page_pool_put_full_page(struct page_pool * pp,struct page * page,bool allow_direct)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
linuxkpi_page_pool_get_dma_addr(const struct page * page)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