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