qed_chain.c (8659B)
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2/* Copyright (c) 2020 Marvell International Ltd. */ 3 4#include <linux/dma-mapping.h> 5#include <linux/qed/qed_chain.h> 6#include <linux/vmalloc.h> 7 8#include "qed_dev_api.h" 9 10static void qed_chain_init(struct qed_chain *chain, 11 const struct qed_chain_init_params *params, 12 u32 page_cnt) 13{ 14 memset(chain, 0, sizeof(*chain)); 15 16 chain->elem_size = params->elem_size; 17 chain->intended_use = params->intended_use; 18 chain->mode = params->mode; 19 chain->cnt_type = params->cnt_type; 20 21 chain->elem_per_page = ELEMS_PER_PAGE(params->elem_size, 22 params->page_size); 23 chain->usable_per_page = USABLE_ELEMS_PER_PAGE(params->elem_size, 24 params->page_size, 25 params->mode); 26 chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(params->elem_size, 27 params->mode); 28 29 chain->elem_per_page_mask = chain->elem_per_page - 1; 30 chain->next_page_mask = chain->usable_per_page & 31 chain->elem_per_page_mask; 32 33 chain->page_size = params->page_size; 34 chain->page_cnt = page_cnt; 35 chain->capacity = chain->usable_per_page * page_cnt; 36 chain->size = chain->elem_per_page * page_cnt; 37 38 if (params->ext_pbl_virt) { 39 chain->pbl_sp.table_virt = params->ext_pbl_virt; 40 chain->pbl_sp.table_phys = params->ext_pbl_phys; 41 42 chain->b_external_pbl = true; 43 } 44} 45 46static void qed_chain_init_next_ptr_elem(const struct qed_chain *chain, 47 void *virt_curr, void *virt_next, 48 dma_addr_t phys_next) 49{ 50 struct qed_chain_next *next; 51 u32 size; 52 53 size = chain->elem_size * chain->usable_per_page; 54 next = virt_curr + size; 55 56 DMA_REGPAIR_LE(next->next_phys, phys_next); 57 next->next_virt = virt_next; 58} 59 60static void qed_chain_init_mem(struct qed_chain *chain, void *virt_addr, 61 dma_addr_t phys_addr) 62{ 63 chain->p_virt_addr = virt_addr; 64 chain->p_phys_addr = phys_addr; 65} 66 67static void qed_chain_free_next_ptr(struct qed_dev *cdev, 68 struct qed_chain *chain) 69{ 70 struct device *dev = &cdev->pdev->dev; 71 struct qed_chain_next *next; 72 dma_addr_t phys, phys_next; 73 void *virt, *virt_next; 74 u32 size, i; 75 76 size = chain->elem_size * chain->usable_per_page; 77 virt = chain->p_virt_addr; 78 phys = chain->p_phys_addr; 79 80 for (i = 0; i < chain->page_cnt; i++) { 81 if (!virt) 82 break; 83 84 next = virt + size; 85 virt_next = next->next_virt; 86 phys_next = HILO_DMA_REGPAIR(next->next_phys); 87 88 dma_free_coherent(dev, chain->page_size, virt, phys); 89 90 virt = virt_next; 91 phys = phys_next; 92 } 93} 94 95static void qed_chain_free_single(struct qed_dev *cdev, 96 struct qed_chain *chain) 97{ 98 if (!chain->p_virt_addr) 99 return; 100 101 dma_free_coherent(&cdev->pdev->dev, chain->page_size, 102 chain->p_virt_addr, chain->p_phys_addr); 103} 104 105static void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *chain) 106{ 107 struct device *dev = &cdev->pdev->dev; 108 struct addr_tbl_entry *entry; 109 u32 i; 110 111 if (!chain->pbl.pp_addr_tbl) 112 return; 113 114 for (i = 0; i < chain->page_cnt; i++) { 115 entry = chain->pbl.pp_addr_tbl + i; 116 if (!entry->virt_addr) 117 break; 118 119 dma_free_coherent(dev, chain->page_size, entry->virt_addr, 120 entry->dma_map); 121 } 122 123 if (!chain->b_external_pbl) 124 dma_free_coherent(dev, chain->pbl_sp.table_size, 125 chain->pbl_sp.table_virt, 126 chain->pbl_sp.table_phys); 127 128 vfree(chain->pbl.pp_addr_tbl); 129 chain->pbl.pp_addr_tbl = NULL; 130} 131 132/** 133 * qed_chain_free() - Free chain DMA memory. 134 * 135 * @cdev: Main device structure. 136 * @chain: Chain to free. 137 */ 138void qed_chain_free(struct qed_dev *cdev, struct qed_chain *chain) 139{ 140 switch (chain->mode) { 141 case QED_CHAIN_MODE_NEXT_PTR: 142 qed_chain_free_next_ptr(cdev, chain); 143 break; 144 case QED_CHAIN_MODE_SINGLE: 145 qed_chain_free_single(cdev, chain); 146 break; 147 case QED_CHAIN_MODE_PBL: 148 qed_chain_free_pbl(cdev, chain); 149 break; 150 default: 151 return; 152 } 153 154 qed_chain_init_mem(chain, NULL, 0); 155} 156 157static int 158qed_chain_alloc_sanity_check(struct qed_dev *cdev, 159 const struct qed_chain_init_params *params, 160 u32 page_cnt) 161{ 162 u64 chain_size; 163 164 chain_size = ELEMS_PER_PAGE(params->elem_size, params->page_size); 165 chain_size *= page_cnt; 166 167 if (!chain_size) 168 return -EINVAL; 169 170 /* The actual chain size can be larger than the maximal possible value 171 * after rounding up the requested elements number to pages, and after 172 * taking into account the unusuable elements (next-ptr elements). 173 * The size of a "u16" chain can be (U16_MAX + 1) since the chain 174 * size/capacity fields are of u32 type. 175 */ 176 switch (params->cnt_type) { 177 case QED_CHAIN_CNT_TYPE_U16: 178 if (chain_size > U16_MAX + 1) 179 break; 180 181 return 0; 182 case QED_CHAIN_CNT_TYPE_U32: 183 if (chain_size > U32_MAX) 184 break; 185 186 return 0; 187 default: 188 return -EINVAL; 189 } 190 191 DP_NOTICE(cdev, 192 "The actual chain size (0x%llx) is larger than the maximal possible value\n", 193 chain_size); 194 195 return -EINVAL; 196} 197 198static int qed_chain_alloc_next_ptr(struct qed_dev *cdev, 199 struct qed_chain *chain) 200{ 201 struct device *dev = &cdev->pdev->dev; 202 void *virt, *virt_prev = NULL; 203 dma_addr_t phys; 204 u32 i; 205 206 for (i = 0; i < chain->page_cnt; i++) { 207 virt = dma_alloc_coherent(dev, chain->page_size, &phys, 208 GFP_KERNEL); 209 if (!virt) 210 return -ENOMEM; 211 212 if (i == 0) { 213 qed_chain_init_mem(chain, virt, phys); 214 qed_chain_reset(chain); 215 } else { 216 qed_chain_init_next_ptr_elem(chain, virt_prev, virt, 217 phys); 218 } 219 220 virt_prev = virt; 221 } 222 223 /* Last page's next element should point to the beginning of the 224 * chain. 225 */ 226 qed_chain_init_next_ptr_elem(chain, virt_prev, chain->p_virt_addr, 227 chain->p_phys_addr); 228 229 return 0; 230} 231 232static int qed_chain_alloc_single(struct qed_dev *cdev, 233 struct qed_chain *chain) 234{ 235 dma_addr_t phys; 236 void *virt; 237 238 virt = dma_alloc_coherent(&cdev->pdev->dev, chain->page_size, 239 &phys, GFP_KERNEL); 240 if (!virt) 241 return -ENOMEM; 242 243 qed_chain_init_mem(chain, virt, phys); 244 qed_chain_reset(chain); 245 246 return 0; 247} 248 249static int qed_chain_alloc_pbl(struct qed_dev *cdev, struct qed_chain *chain) 250{ 251 struct device *dev = &cdev->pdev->dev; 252 struct addr_tbl_entry *addr_tbl; 253 dma_addr_t phys, pbl_phys; 254 __le64 *pbl_virt; 255 u32 page_cnt, i; 256 size_t size; 257 void *virt; 258 259 page_cnt = chain->page_cnt; 260 261 size = array_size(page_cnt, sizeof(*addr_tbl)); 262 if (unlikely(size == SIZE_MAX)) 263 return -EOVERFLOW; 264 265 addr_tbl = vzalloc(size); 266 if (!addr_tbl) 267 return -ENOMEM; 268 269 chain->pbl.pp_addr_tbl = addr_tbl; 270 271 if (chain->b_external_pbl) { 272 pbl_virt = chain->pbl_sp.table_virt; 273 goto alloc_pages; 274 } 275 276 size = array_size(page_cnt, sizeof(*pbl_virt)); 277 if (unlikely(size == SIZE_MAX)) 278 return -EOVERFLOW; 279 280 pbl_virt = dma_alloc_coherent(dev, size, &pbl_phys, GFP_KERNEL); 281 if (!pbl_virt) 282 return -ENOMEM; 283 284 chain->pbl_sp.table_virt = pbl_virt; 285 chain->pbl_sp.table_phys = pbl_phys; 286 chain->pbl_sp.table_size = size; 287 288alloc_pages: 289 for (i = 0; i < page_cnt; i++) { 290 virt = dma_alloc_coherent(dev, chain->page_size, &phys, 291 GFP_KERNEL); 292 if (!virt) 293 return -ENOMEM; 294 295 if (i == 0) { 296 qed_chain_init_mem(chain, virt, phys); 297 qed_chain_reset(chain); 298 } 299 300 /* Fill the PBL table with the physical address of the page */ 301 pbl_virt[i] = cpu_to_le64(phys); 302 303 /* Keep the virtual address of the page */ 304 addr_tbl[i].virt_addr = virt; 305 addr_tbl[i].dma_map = phys; 306 } 307 308 return 0; 309} 310 311/** 312 * qed_chain_alloc() - Allocate and initialize a chain. 313 * 314 * @cdev: Main device structure. 315 * @chain: Chain to be processed. 316 * @params: Chain initialization parameters. 317 * 318 * Return: 0 on success, negative errno otherwise. 319 */ 320int qed_chain_alloc(struct qed_dev *cdev, struct qed_chain *chain, 321 struct qed_chain_init_params *params) 322{ 323 u32 page_cnt; 324 int rc; 325 326 if (!params->page_size) 327 params->page_size = QED_CHAIN_PAGE_SIZE; 328 329 if (params->mode == QED_CHAIN_MODE_SINGLE) 330 page_cnt = 1; 331 else 332 page_cnt = QED_CHAIN_PAGE_CNT(params->num_elems, 333 params->elem_size, 334 params->page_size, 335 params->mode); 336 337 rc = qed_chain_alloc_sanity_check(cdev, params, page_cnt); 338 if (rc) { 339 DP_NOTICE(cdev, 340 "Cannot allocate a chain with the given arguments:\n"); 341 DP_NOTICE(cdev, 342 "[use_mode %d, mode %d, cnt_type %d, num_elems %d, elem_size %zu, page_size %u]\n", 343 params->intended_use, params->mode, params->cnt_type, 344 params->num_elems, params->elem_size, 345 params->page_size); 346 return rc; 347 } 348 349 qed_chain_init(chain, params, page_cnt); 350 351 switch (params->mode) { 352 case QED_CHAIN_MODE_NEXT_PTR: 353 rc = qed_chain_alloc_next_ptr(cdev, chain); 354 break; 355 case QED_CHAIN_MODE_SINGLE: 356 rc = qed_chain_alloc_single(cdev, chain); 357 break; 358 case QED_CHAIN_MODE_PBL: 359 rc = qed_chain_alloc_pbl(cdev, chain); 360 break; 361 default: 362 return -EINVAL; 363 } 364 365 if (!rc) 366 return 0; 367 368 qed_chain_free(cdev, chain); 369 370 return rc; 371}