xref: /dpdk/drivers/dma/idxd/idxd_common.c (revision f665790a5dbad7b645ff46f31d65e977324e7bfc)
1e33ad06eSKevin Laatz /* SPDX-License-Identifier: BSD-3-Clause
2e33ad06eSKevin Laatz  * Copyright 2021 Intel Corporation
3e33ad06eSKevin Laatz  */
4e33ad06eSKevin Laatz 
53d36a0a1SKevin Laatz #include <x86intrin.h>
63d36a0a1SKevin Laatz 
755dc0f60SKevin Laatz #include <rte_malloc.h>
855dc0f60SKevin Laatz #include <rte_common.h>
9e33ad06eSKevin Laatz #include <rte_log.h>
103d36a0a1SKevin Laatz #include <rte_prefetch.h>
11e33ad06eSKevin Laatz 
12e33ad06eSKevin Laatz #include "idxd_internal.h"
13e33ad06eSKevin Laatz 
1455dc0f60SKevin Laatz #define IDXD_PMD_NAME_STR "dmadev_idxd"
1555dc0f60SKevin Laatz 
16aa802b10SBruce Richardson /* systems with DSA all support AVX2 so allow our data-path functions to
17aa802b10SBruce Richardson  * always use at least that instruction set
18aa802b10SBruce Richardson  */
19aa802b10SBruce Richardson #ifndef __AVX2__
20aa802b10SBruce Richardson #define __use_avx2 __attribute__((target("avx2")))
21aa802b10SBruce Richardson #else
22aa802b10SBruce Richardson #define __use_avx2
23aa802b10SBruce Richardson #endif
24aa802b10SBruce Richardson 
25aa802b10SBruce Richardson __use_avx2
263d36a0a1SKevin Laatz static __rte_always_inline rte_iova_t
273d36a0a1SKevin Laatz __desc_idx_to_iova(struct idxd_dmadev *idxd, uint16_t n)
283d36a0a1SKevin Laatz {
293d36a0a1SKevin Laatz 	return idxd->desc_iova + (n * sizeof(struct idxd_hw_desc));
303d36a0a1SKevin Laatz }
313d36a0a1SKevin Laatz 
32aa802b10SBruce Richardson __use_avx2
333d36a0a1SKevin Laatz static __rte_always_inline void
343d36a0a1SKevin Laatz __idxd_movdir64b(volatile void *dst, const struct idxd_hw_desc *src)
353d36a0a1SKevin Laatz {
363d36a0a1SKevin Laatz 	asm volatile (".byte 0x66, 0x0f, 0x38, 0xf8, 0x02"
373d36a0a1SKevin Laatz 			:
383d36a0a1SKevin Laatz 			: "a" (dst), "d" (src)
393d36a0a1SKevin Laatz 			: "memory");
403d36a0a1SKevin Laatz }
413d36a0a1SKevin Laatz 
42aa802b10SBruce Richardson __use_avx2
433d36a0a1SKevin Laatz static __rte_always_inline void
443d36a0a1SKevin Laatz __submit(struct idxd_dmadev *idxd)
453d36a0a1SKevin Laatz {
463d36a0a1SKevin Laatz 	rte_prefetch1(&idxd->batch_comp_ring[idxd->batch_idx_read]);
473d36a0a1SKevin Laatz 
483d36a0a1SKevin Laatz 	if (idxd->batch_size == 0)
493d36a0a1SKevin Laatz 		return;
503d36a0a1SKevin Laatz 
513d36a0a1SKevin Laatz 	/* write completion to batch comp ring */
523d36a0a1SKevin Laatz 	rte_iova_t comp_addr = idxd->batch_iova +
533d36a0a1SKevin Laatz 			(idxd->batch_idx_write * sizeof(struct idxd_completion));
543d36a0a1SKevin Laatz 
553d36a0a1SKevin Laatz 	if (idxd->batch_size == 1) {
563d36a0a1SKevin Laatz 		/* submit batch directly */
573d36a0a1SKevin Laatz 		struct idxd_hw_desc desc =
583d36a0a1SKevin Laatz 				idxd->desc_ring[idxd->batch_start & idxd->desc_ring_mask];
593d36a0a1SKevin Laatz 		desc.completion = comp_addr;
603d36a0a1SKevin Laatz 		desc.op_flags |= IDXD_FLAG_REQUEST_COMPLETION;
613d36a0a1SKevin Laatz 		_mm_sfence(); /* fence before writing desc to device */
623d36a0a1SKevin Laatz 		__idxd_movdir64b(idxd->portal, &desc);
633d36a0a1SKevin Laatz 	} else {
643d36a0a1SKevin Laatz 		const struct idxd_hw_desc batch_desc = {
653d36a0a1SKevin Laatz 				.op_flags = (idxd_op_batch << IDXD_CMD_OP_SHIFT) |
663d36a0a1SKevin Laatz 				IDXD_FLAG_COMPLETION_ADDR_VALID |
673d36a0a1SKevin Laatz 				IDXD_FLAG_REQUEST_COMPLETION,
683d36a0a1SKevin Laatz 				.desc_addr = __desc_idx_to_iova(idxd,
693d36a0a1SKevin Laatz 						idxd->batch_start & idxd->desc_ring_mask),
703d36a0a1SKevin Laatz 				.completion = comp_addr,
713d36a0a1SKevin Laatz 				.size = idxd->batch_size,
723d36a0a1SKevin Laatz 		};
733d36a0a1SKevin Laatz 		_mm_sfence(); /* fence before writing desc to device */
743d36a0a1SKevin Laatz 		__idxd_movdir64b(idxd->portal, &batch_desc);
753d36a0a1SKevin Laatz 	}
763d36a0a1SKevin Laatz 
773d36a0a1SKevin Laatz 	if (++idxd->batch_idx_write > idxd->max_batches)
783d36a0a1SKevin Laatz 		idxd->batch_idx_write = 0;
793d36a0a1SKevin Laatz 
80280c3ca0SKevin Laatz 	idxd->stats.submitted += idxd->batch_size;
81280c3ca0SKevin Laatz 
823d36a0a1SKevin Laatz 	idxd->batch_start += idxd->batch_size;
833d36a0a1SKevin Laatz 	idxd->batch_size = 0;
843d36a0a1SKevin Laatz 	idxd->batch_idx_ring[idxd->batch_idx_write] = idxd->batch_start;
853d36a0a1SKevin Laatz 	_mm256_store_si256((void *)&idxd->batch_comp_ring[idxd->batch_idx_write],
863d36a0a1SKevin Laatz 			_mm256_setzero_si256());
873d36a0a1SKevin Laatz }
883d36a0a1SKevin Laatz 
89aa802b10SBruce Richardson __use_avx2
903d36a0a1SKevin Laatz static __rte_always_inline int
913d36a0a1SKevin Laatz __idxd_write_desc(struct idxd_dmadev *idxd,
923d36a0a1SKevin Laatz 		const uint32_t op_flags,
933d36a0a1SKevin Laatz 		const rte_iova_t src,
943d36a0a1SKevin Laatz 		const rte_iova_t dst,
953d36a0a1SKevin Laatz 		const uint32_t size,
963d36a0a1SKevin Laatz 		const uint32_t flags)
973d36a0a1SKevin Laatz {
983d36a0a1SKevin Laatz 	uint16_t mask = idxd->desc_ring_mask;
993d36a0a1SKevin Laatz 	uint16_t job_id = idxd->batch_start + idxd->batch_size;
1003d36a0a1SKevin Laatz 	/* we never wrap batches, so we only mask the start and allow start+size to overflow */
1013d36a0a1SKevin Laatz 	uint16_t write_idx = (idxd->batch_start & mask) + idxd->batch_size;
1023d36a0a1SKevin Laatz 
1033d36a0a1SKevin Laatz 	/* first check batch ring space then desc ring space */
1043d36a0a1SKevin Laatz 	if ((idxd->batch_idx_read == 0 && idxd->batch_idx_write == idxd->max_batches) ||
1053d36a0a1SKevin Laatz 			idxd->batch_idx_write + 1 == idxd->batch_idx_read)
1063d36a0a1SKevin Laatz 		return -ENOSPC;
1073d36a0a1SKevin Laatz 	if (((write_idx + 1) & mask) == (idxd->ids_returned & mask))
1083d36a0a1SKevin Laatz 		return -ENOSPC;
1093d36a0a1SKevin Laatz 
1103d36a0a1SKevin Laatz 	/* write desc. Note: descriptors don't wrap, but the completion address does */
1113d36a0a1SKevin Laatz 	const uint64_t op_flags64 = (uint64_t)(op_flags | IDXD_FLAG_COMPLETION_ADDR_VALID) << 32;
1123d36a0a1SKevin Laatz 	const uint64_t comp_addr = __desc_idx_to_iova(idxd, write_idx & mask);
1133d36a0a1SKevin Laatz 	_mm256_store_si256((void *)&idxd->desc_ring[write_idx],
1143d36a0a1SKevin Laatz 			_mm256_set_epi64x(dst, src, comp_addr, op_flags64));
1153d36a0a1SKevin Laatz 	_mm256_store_si256((void *)&idxd->desc_ring[write_idx].size,
1163d36a0a1SKevin Laatz 			_mm256_set_epi64x(0, 0, 0, size));
1173d36a0a1SKevin Laatz 
1183d36a0a1SKevin Laatz 	idxd->batch_size++;
1193d36a0a1SKevin Laatz 
1203d36a0a1SKevin Laatz 	rte_prefetch0_write(&idxd->desc_ring[write_idx + 1]);
1213d36a0a1SKevin Laatz 
1223d36a0a1SKevin Laatz 	if (flags & RTE_DMA_OP_FLAG_SUBMIT)
1233d36a0a1SKevin Laatz 		__submit(idxd);
1243d36a0a1SKevin Laatz 
1253d36a0a1SKevin Laatz 	return job_id;
1263d36a0a1SKevin Laatz }
1273d36a0a1SKevin Laatz 
128aa802b10SBruce Richardson __use_avx2
1293d36a0a1SKevin Laatz int
1303d36a0a1SKevin Laatz idxd_enqueue_copy(void *dev_private, uint16_t qid __rte_unused, rte_iova_t src,
1313d36a0a1SKevin Laatz 		rte_iova_t dst, unsigned int length, uint64_t flags)
1323d36a0a1SKevin Laatz {
1333d36a0a1SKevin Laatz 	/* we can take advantage of the fact that the fence flag in dmadev and DSA are the same,
1343d36a0a1SKevin Laatz 	 * but check it at compile time to be sure.
1353d36a0a1SKevin Laatz 	 */
1363d36a0a1SKevin Laatz 	RTE_BUILD_BUG_ON(RTE_DMA_OP_FLAG_FENCE != IDXD_FLAG_FENCE);
1373d36a0a1SKevin Laatz 	uint32_t memmove = (idxd_op_memmove << IDXD_CMD_OP_SHIFT) |
1383d36a0a1SKevin Laatz 			IDXD_FLAG_CACHE_CONTROL | (flags & IDXD_FLAG_FENCE);
1393d36a0a1SKevin Laatz 	return __idxd_write_desc(dev_private, memmove, src, dst, length,
1403d36a0a1SKevin Laatz 			flags);
1413d36a0a1SKevin Laatz }
1423d36a0a1SKevin Laatz 
143aa802b10SBruce Richardson __use_avx2
1443d36a0a1SKevin Laatz int
1453d36a0a1SKevin Laatz idxd_enqueue_fill(void *dev_private, uint16_t qid __rte_unused, uint64_t pattern,
1463d36a0a1SKevin Laatz 		rte_iova_t dst, unsigned int length, uint64_t flags)
1473d36a0a1SKevin Laatz {
1483d36a0a1SKevin Laatz 	uint32_t fill = (idxd_op_fill << IDXD_CMD_OP_SHIFT) |
1493d36a0a1SKevin Laatz 			IDXD_FLAG_CACHE_CONTROL | (flags & IDXD_FLAG_FENCE);
1503d36a0a1SKevin Laatz 	return __idxd_write_desc(dev_private, fill, pattern, dst, length,
1513d36a0a1SKevin Laatz 			flags);
1523d36a0a1SKevin Laatz }
1533d36a0a1SKevin Laatz 
154aa802b10SBruce Richardson __use_avx2
1553d36a0a1SKevin Laatz int
1563d36a0a1SKevin Laatz idxd_submit(void *dev_private, uint16_t qid __rte_unused)
1573d36a0a1SKevin Laatz {
1583d36a0a1SKevin Laatz 	__submit(dev_private);
1593d36a0a1SKevin Laatz 	return 0;
1603d36a0a1SKevin Laatz }
1613d36a0a1SKevin Laatz 
162aa802b10SBruce Richardson __use_avx2
16397aeed56SKevin Laatz static enum rte_dma_status_code
16497aeed56SKevin Laatz get_comp_status(struct idxd_completion *c)
16597aeed56SKevin Laatz {
16697aeed56SKevin Laatz 	uint8_t st = c->status;
16797aeed56SKevin Laatz 	switch (st) {
16897aeed56SKevin Laatz 	/* successful descriptors are not written back normally */
16997aeed56SKevin Laatz 	case IDXD_COMP_STATUS_INCOMPLETE:
17097aeed56SKevin Laatz 	case IDXD_COMP_STATUS_SUCCESS:
17197aeed56SKevin Laatz 		return RTE_DMA_STATUS_SUCCESSFUL;
172fe1a5a9bSSean Morrissey 	case IDXD_COMP_STATUS_PAGE_FAULT:
173fe1a5a9bSSean Morrissey 		return RTE_DMA_STATUS_PAGE_FAULT;
17497aeed56SKevin Laatz 	case IDXD_COMP_STATUS_INVALID_OPCODE:
17597aeed56SKevin Laatz 		return RTE_DMA_STATUS_INVALID_OPCODE;
17697aeed56SKevin Laatz 	case IDXD_COMP_STATUS_INVALID_SIZE:
17797aeed56SKevin Laatz 		return RTE_DMA_STATUS_INVALID_LENGTH;
17897aeed56SKevin Laatz 	case IDXD_COMP_STATUS_SKIPPED:
17997aeed56SKevin Laatz 		return RTE_DMA_STATUS_NOT_ATTEMPTED;
18097aeed56SKevin Laatz 	default:
18197aeed56SKevin Laatz 		return RTE_DMA_STATUS_ERROR_UNKNOWN;
18297aeed56SKevin Laatz 	}
18397aeed56SKevin Laatz }
18497aeed56SKevin Laatz 
185aa802b10SBruce Richardson __use_avx2
1865a23df34SKevin Laatz int
1875a23df34SKevin Laatz idxd_vchan_status(const struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
1885a23df34SKevin Laatz 		enum rte_dma_vchan_status *status)
1895a23df34SKevin Laatz {
1905a23df34SKevin Laatz 	struct idxd_dmadev *idxd = dev->fp_obj->dev_private;
1915a23df34SKevin Laatz 	uint16_t last_batch_write = idxd->batch_idx_write == 0 ? idxd->max_batches :
1925a23df34SKevin Laatz 			idxd->batch_idx_write - 1;
1935a23df34SKevin Laatz 	uint8_t bstatus = (idxd->batch_comp_ring[last_batch_write].status != 0);
1945a23df34SKevin Laatz 
1955a23df34SKevin Laatz 	/* An IDXD device will always be either active or idle.
1965a23df34SKevin Laatz 	 * RTE_DMA_VCHAN_HALTED_ERROR is therefore not supported by IDXD.
1975a23df34SKevin Laatz 	 */
1985a23df34SKevin Laatz 	*status = bstatus ? RTE_DMA_VCHAN_IDLE : RTE_DMA_VCHAN_ACTIVE;
1995a23df34SKevin Laatz 
2005a23df34SKevin Laatz 	return 0;
2015a23df34SKevin Laatz }
2025a23df34SKevin Laatz 
203aa802b10SBruce Richardson __use_avx2
20497aeed56SKevin Laatz static __rte_always_inline int
20597aeed56SKevin Laatz batch_ok(struct idxd_dmadev *idxd, uint16_t max_ops, enum rte_dma_status_code *status)
20697aeed56SKevin Laatz {
20797aeed56SKevin Laatz 	uint16_t ret;
20897aeed56SKevin Laatz 	uint8_t bstatus;
20997aeed56SKevin Laatz 
21097aeed56SKevin Laatz 	if (max_ops == 0)
21197aeed56SKevin Laatz 		return 0;
21297aeed56SKevin Laatz 
21397aeed56SKevin Laatz 	/* first check if there are any unreturned handles from last time */
21497aeed56SKevin Laatz 	if (idxd->ids_avail != idxd->ids_returned) {
21597aeed56SKevin Laatz 		ret = RTE_MIN((uint16_t)(idxd->ids_avail - idxd->ids_returned), max_ops);
21697aeed56SKevin Laatz 		idxd->ids_returned += ret;
21797aeed56SKevin Laatz 		if (status)
21897aeed56SKevin Laatz 			memset(status, RTE_DMA_STATUS_SUCCESSFUL, ret * sizeof(*status));
21997aeed56SKevin Laatz 		return ret;
22097aeed56SKevin Laatz 	}
22197aeed56SKevin Laatz 
22297aeed56SKevin Laatz 	if (idxd->batch_idx_read == idxd->batch_idx_write)
22397aeed56SKevin Laatz 		return 0;
22497aeed56SKevin Laatz 
22597aeed56SKevin Laatz 	bstatus = idxd->batch_comp_ring[idxd->batch_idx_read].status;
22697aeed56SKevin Laatz 	/* now check if next batch is complete and successful */
22797aeed56SKevin Laatz 	if (bstatus == IDXD_COMP_STATUS_SUCCESS) {
22897aeed56SKevin Laatz 		/* since the batch idx ring stores the start of each batch, pre-increment to lookup
22997aeed56SKevin Laatz 		 * start of next batch.
23097aeed56SKevin Laatz 		 */
23197aeed56SKevin Laatz 		if (++idxd->batch_idx_read > idxd->max_batches)
23297aeed56SKevin Laatz 			idxd->batch_idx_read = 0;
23397aeed56SKevin Laatz 		idxd->ids_avail = idxd->batch_idx_ring[idxd->batch_idx_read];
23497aeed56SKevin Laatz 
23597aeed56SKevin Laatz 		ret = RTE_MIN((uint16_t)(idxd->ids_avail - idxd->ids_returned), max_ops);
23697aeed56SKevin Laatz 		idxd->ids_returned += ret;
23797aeed56SKevin Laatz 		if (status)
23897aeed56SKevin Laatz 			memset(status, RTE_DMA_STATUS_SUCCESSFUL, ret * sizeof(*status));
23997aeed56SKevin Laatz 		return ret;
24097aeed56SKevin Laatz 	}
24197aeed56SKevin Laatz 	/* check if batch is incomplete */
24297aeed56SKevin Laatz 	else if (bstatus == IDXD_COMP_STATUS_INCOMPLETE)
24397aeed56SKevin Laatz 		return 0;
24497aeed56SKevin Laatz 
24597aeed56SKevin Laatz 	return -1; /* error case */
24697aeed56SKevin Laatz }
24797aeed56SKevin Laatz 
248aa802b10SBruce Richardson __use_avx2
24997aeed56SKevin Laatz static inline uint16_t
25097aeed56SKevin Laatz batch_completed(struct idxd_dmadev *idxd, uint16_t max_ops, bool *has_error)
25197aeed56SKevin Laatz {
25297aeed56SKevin Laatz 	uint16_t i;
25397aeed56SKevin Laatz 	uint16_t b_start, b_end, next_batch;
25497aeed56SKevin Laatz 
25597aeed56SKevin Laatz 	int ret = batch_ok(idxd, max_ops, NULL);
25697aeed56SKevin Laatz 	if (ret >= 0)
25797aeed56SKevin Laatz 		return ret;
25897aeed56SKevin Laatz 
25997aeed56SKevin Laatz 	/* ERROR case, not successful, not incomplete */
26097aeed56SKevin Laatz 	/* Get the batch size, and special case size 1.
26197aeed56SKevin Laatz 	 * once we identify the actual failure job, return other jobs, then update
26297aeed56SKevin Laatz 	 * the batch ring indexes to make it look like the first job of the batch has failed.
26397aeed56SKevin Laatz 	 * Subsequent calls here will always return zero packets, and the error must be cleared by
26497aeed56SKevin Laatz 	 * calling the completed_status() function.
26597aeed56SKevin Laatz 	 */
26697aeed56SKevin Laatz 	next_batch = (idxd->batch_idx_read + 1);
26797aeed56SKevin Laatz 	if (next_batch > idxd->max_batches)
26897aeed56SKevin Laatz 		next_batch = 0;
26997aeed56SKevin Laatz 	b_start = idxd->batch_idx_ring[idxd->batch_idx_read];
27097aeed56SKevin Laatz 	b_end = idxd->batch_idx_ring[next_batch];
27197aeed56SKevin Laatz 
27297aeed56SKevin Laatz 	if (b_end - b_start == 1) { /* not a batch */
27397aeed56SKevin Laatz 		*has_error = true;
27497aeed56SKevin Laatz 		return 0;
27597aeed56SKevin Laatz 	}
27697aeed56SKevin Laatz 
27797aeed56SKevin Laatz 	for (i = b_start; i < b_end; i++) {
27897aeed56SKevin Laatz 		struct idxd_completion *c = (void *)&idxd->desc_ring[i & idxd->desc_ring_mask];
27997aeed56SKevin Laatz 		if (c->status > IDXD_COMP_STATUS_SUCCESS) /* ignore incomplete(0) and success(1) */
28097aeed56SKevin Laatz 			break;
28197aeed56SKevin Laatz 	}
28297aeed56SKevin Laatz 	ret = RTE_MIN((uint16_t)(i - idxd->ids_returned), max_ops);
28397aeed56SKevin Laatz 	if (ret < max_ops)
28497aeed56SKevin Laatz 		*has_error = true; /* we got up to the point of error */
28597aeed56SKevin Laatz 	idxd->ids_avail = idxd->ids_returned += ret;
28697aeed56SKevin Laatz 
28797aeed56SKevin Laatz 	/* to ensure we can call twice and just return 0, set start of batch to where we finished */
28897aeed56SKevin Laatz 	idxd->batch_comp_ring[idxd->batch_idx_read].completed_size -= ret;
28997aeed56SKevin Laatz 	idxd->batch_idx_ring[idxd->batch_idx_read] += ret;
29097aeed56SKevin Laatz 	if (idxd->batch_idx_ring[next_batch] - idxd->batch_idx_ring[idxd->batch_idx_read] == 1) {
29197aeed56SKevin Laatz 		/* copy over the descriptor status to the batch ring as if no batch */
29297aeed56SKevin Laatz 		uint16_t d_idx = idxd->batch_idx_ring[idxd->batch_idx_read] & idxd->desc_ring_mask;
29397aeed56SKevin Laatz 		struct idxd_completion *desc_comp = (void *)&idxd->desc_ring[d_idx];
29497aeed56SKevin Laatz 		idxd->batch_comp_ring[idxd->batch_idx_read].status = desc_comp->status;
29597aeed56SKevin Laatz 	}
29697aeed56SKevin Laatz 
29797aeed56SKevin Laatz 	return ret;
29897aeed56SKevin Laatz }
29997aeed56SKevin Laatz 
300aa802b10SBruce Richardson __use_avx2
30197aeed56SKevin Laatz static uint16_t
30297aeed56SKevin Laatz batch_completed_status(struct idxd_dmadev *idxd, uint16_t max_ops, enum rte_dma_status_code *status)
30397aeed56SKevin Laatz {
30497aeed56SKevin Laatz 	uint16_t next_batch;
30597aeed56SKevin Laatz 
30697aeed56SKevin Laatz 	int ret = batch_ok(idxd, max_ops, status);
30797aeed56SKevin Laatz 	if (ret >= 0)
30897aeed56SKevin Laatz 		return ret;
30997aeed56SKevin Laatz 
31097aeed56SKevin Laatz 	/* ERROR case, not successful, not incomplete */
31197aeed56SKevin Laatz 	/* Get the batch size, and special case size 1.
31297aeed56SKevin Laatz 	 */
31397aeed56SKevin Laatz 	next_batch = (idxd->batch_idx_read + 1);
31497aeed56SKevin Laatz 	if (next_batch > idxd->max_batches)
31597aeed56SKevin Laatz 		next_batch = 0;
31697aeed56SKevin Laatz 	const uint16_t b_start = idxd->batch_idx_ring[idxd->batch_idx_read];
31797aeed56SKevin Laatz 	const uint16_t b_end = idxd->batch_idx_ring[next_batch];
31897aeed56SKevin Laatz 	const uint16_t b_len = b_end - b_start;
31997aeed56SKevin Laatz 	if (b_len == 1) {/* not a batch */
32097aeed56SKevin Laatz 		*status = get_comp_status(&idxd->batch_comp_ring[idxd->batch_idx_read]);
321280c3ca0SKevin Laatz 		if (status != RTE_DMA_STATUS_SUCCESSFUL)
322280c3ca0SKevin Laatz 			idxd->stats.errors++;
32397aeed56SKevin Laatz 		idxd->ids_avail++;
32497aeed56SKevin Laatz 		idxd->ids_returned++;
32597aeed56SKevin Laatz 		idxd->batch_idx_read = next_batch;
32697aeed56SKevin Laatz 		return 1;
32797aeed56SKevin Laatz 	}
32897aeed56SKevin Laatz 
32997aeed56SKevin Laatz 	/* not a single-element batch, need to process more.
33097aeed56SKevin Laatz 	 * Scenarios:
33197aeed56SKevin Laatz 	 * 1. max_ops >= batch_size - can fit everything, simple case
33297aeed56SKevin Laatz 	 *   - loop through completed ops and then add on any not-attempted ones
33397aeed56SKevin Laatz 	 * 2. max_ops < batch_size - can't fit everything, more complex case
33497aeed56SKevin Laatz 	 *   - loop through completed/incomplete and stop when hit max_ops
33597aeed56SKevin Laatz 	 *   - adjust the batch descriptor to update where we stopped, with appropriate bcount
33697aeed56SKevin Laatz 	 *   - if bcount is to be exactly 1, update the batch descriptor as it will be treated as
33797aeed56SKevin Laatz 	 *     non-batch next time.
33897aeed56SKevin Laatz 	 */
33997aeed56SKevin Laatz 	const uint16_t bcount = idxd->batch_comp_ring[idxd->batch_idx_read].completed_size;
34097aeed56SKevin Laatz 	for (ret = 0; ret < b_len && ret < max_ops; ret++) {
34197aeed56SKevin Laatz 		struct idxd_completion *c = (void *)
34297aeed56SKevin Laatz 				&idxd->desc_ring[(b_start + ret) & idxd->desc_ring_mask];
34397aeed56SKevin Laatz 		status[ret] = (ret < bcount) ? get_comp_status(c) : RTE_DMA_STATUS_NOT_ATTEMPTED;
344280c3ca0SKevin Laatz 		if (status[ret] != RTE_DMA_STATUS_SUCCESSFUL)
345280c3ca0SKevin Laatz 			idxd->stats.errors++;
34697aeed56SKevin Laatz 	}
34797aeed56SKevin Laatz 	idxd->ids_avail = idxd->ids_returned += ret;
34897aeed56SKevin Laatz 
34997aeed56SKevin Laatz 	/* everything fit */
35097aeed56SKevin Laatz 	if (ret == b_len) {
35197aeed56SKevin Laatz 		idxd->batch_idx_read = next_batch;
35297aeed56SKevin Laatz 		return ret;
35397aeed56SKevin Laatz 	}
35497aeed56SKevin Laatz 
35597aeed56SKevin Laatz 	/* set up for next time, update existing batch descriptor & start idx at batch_idx_read */
35697aeed56SKevin Laatz 	idxd->batch_idx_ring[idxd->batch_idx_read] += ret;
35797aeed56SKevin Laatz 	if (ret > bcount) {
35897aeed56SKevin Laatz 		/* we have only incomplete ones - set batch completed size to 0 */
35997aeed56SKevin Laatz 		struct idxd_completion *comp = &idxd->batch_comp_ring[idxd->batch_idx_read];
36097aeed56SKevin Laatz 		comp->completed_size = 0;
36197aeed56SKevin Laatz 		/* if there is only one descriptor left, job skipped so set flag appropriately */
36297aeed56SKevin Laatz 		if (b_len - ret == 1)
36397aeed56SKevin Laatz 			comp->status = IDXD_COMP_STATUS_SKIPPED;
36497aeed56SKevin Laatz 	} else {
36597aeed56SKevin Laatz 		struct idxd_completion *comp = &idxd->batch_comp_ring[idxd->batch_idx_read];
36697aeed56SKevin Laatz 		comp->completed_size -= ret;
36797aeed56SKevin Laatz 		/* if there is only one descriptor left, copy status info straight to desc */
36897aeed56SKevin Laatz 		if (comp->completed_size == 1) {
36997aeed56SKevin Laatz 			struct idxd_completion *c = (void *)
37097aeed56SKevin Laatz 					&idxd->desc_ring[(b_start + ret) & idxd->desc_ring_mask];
37197aeed56SKevin Laatz 			comp->status = c->status;
37297aeed56SKevin Laatz 			/* individual descs can be ok without writeback, but not batches */
37397aeed56SKevin Laatz 			if (comp->status == IDXD_COMP_STATUS_INCOMPLETE)
37497aeed56SKevin Laatz 				comp->status = IDXD_COMP_STATUS_SUCCESS;
37597aeed56SKevin Laatz 		} else if (bcount == b_len) {
37697aeed56SKevin Laatz 			/* check if we still have an error, and clear flag if not */
37797aeed56SKevin Laatz 			uint16_t i;
37897aeed56SKevin Laatz 			for (i = b_start + ret; i < b_end; i++) {
37997aeed56SKevin Laatz 				struct idxd_completion *c = (void *)
38097aeed56SKevin Laatz 						&idxd->desc_ring[i & idxd->desc_ring_mask];
38197aeed56SKevin Laatz 				if (c->status > IDXD_COMP_STATUS_SUCCESS)
38297aeed56SKevin Laatz 					break;
38397aeed56SKevin Laatz 			}
38497aeed56SKevin Laatz 			if (i == b_end) /* no errors */
38597aeed56SKevin Laatz 				comp->status = IDXD_COMP_STATUS_SUCCESS;
38697aeed56SKevin Laatz 		}
38797aeed56SKevin Laatz 	}
38897aeed56SKevin Laatz 
38997aeed56SKevin Laatz 	return ret;
39097aeed56SKevin Laatz }
39197aeed56SKevin Laatz 
392aa802b10SBruce Richardson __use_avx2
39397aeed56SKevin Laatz uint16_t
39497aeed56SKevin Laatz idxd_completed(void *dev_private, uint16_t qid __rte_unused, uint16_t max_ops,
39597aeed56SKevin Laatz 		uint16_t *last_idx, bool *has_error)
39697aeed56SKevin Laatz {
39797aeed56SKevin Laatz 	struct idxd_dmadev *idxd = dev_private;
39897aeed56SKevin Laatz 	uint16_t batch, ret = 0;
39997aeed56SKevin Laatz 
40097aeed56SKevin Laatz 	do {
40197aeed56SKevin Laatz 		batch = batch_completed(idxd, max_ops - ret, has_error);
40297aeed56SKevin Laatz 		ret += batch;
40397aeed56SKevin Laatz 	} while (batch > 0 && *has_error == false);
40497aeed56SKevin Laatz 
405280c3ca0SKevin Laatz 	idxd->stats.completed += ret;
40697aeed56SKevin Laatz 	*last_idx = idxd->ids_returned - 1;
40797aeed56SKevin Laatz 	return ret;
40897aeed56SKevin Laatz }
40997aeed56SKevin Laatz 
410aa802b10SBruce Richardson __use_avx2
41197aeed56SKevin Laatz uint16_t
41297aeed56SKevin Laatz idxd_completed_status(void *dev_private, uint16_t qid __rte_unused, uint16_t max_ops,
41397aeed56SKevin Laatz 		uint16_t *last_idx, enum rte_dma_status_code *status)
41497aeed56SKevin Laatz {
41597aeed56SKevin Laatz 	struct idxd_dmadev *idxd = dev_private;
41697aeed56SKevin Laatz 	uint16_t batch, ret = 0;
41797aeed56SKevin Laatz 
41897aeed56SKevin Laatz 	do {
41997aeed56SKevin Laatz 		batch = batch_completed_status(idxd, max_ops - ret, &status[ret]);
42097aeed56SKevin Laatz 		ret += batch;
42197aeed56SKevin Laatz 	} while (batch > 0);
42297aeed56SKevin Laatz 
423280c3ca0SKevin Laatz 	idxd->stats.completed += ret;
42497aeed56SKevin Laatz 	*last_idx = idxd->ids_returned - 1;
42597aeed56SKevin Laatz 	return ret;
42697aeed56SKevin Laatz }
42797aeed56SKevin Laatz 
42855dc0f60SKevin Laatz int
42982147042SKevin Laatz idxd_dump(const struct rte_dma_dev *dev, FILE *f)
43082147042SKevin Laatz {
43182147042SKevin Laatz 	struct idxd_dmadev *idxd = dev->fp_obj->dev_private;
43282147042SKevin Laatz 	unsigned int i;
43382147042SKevin Laatz 
43482147042SKevin Laatz 	fprintf(f, "== IDXD Private Data ==\n");
43582147042SKevin Laatz 	fprintf(f, "  Portal: %p\n", idxd->portal);
43682147042SKevin Laatz 	fprintf(f, "  Config: { ring_size: %u }\n",
43782147042SKevin Laatz 			idxd->qcfg.nb_desc);
43882147042SKevin Laatz 	fprintf(f, "  Batch ring (sz = %u, max_batches = %u):\n\t",
43982147042SKevin Laatz 			idxd->max_batches + 1, idxd->max_batches);
44082147042SKevin Laatz 	for (i = 0; i <= idxd->max_batches; i++) {
44182147042SKevin Laatz 		fprintf(f, " %u ", idxd->batch_idx_ring[i]);
44282147042SKevin Laatz 		if (i == idxd->batch_idx_read && i == idxd->batch_idx_write)
44382147042SKevin Laatz 			fprintf(f, "[rd ptr, wr ptr] ");
44482147042SKevin Laatz 		else if (i == idxd->batch_idx_read)
44582147042SKevin Laatz 			fprintf(f, "[rd ptr] ");
44682147042SKevin Laatz 		else if (i == idxd->batch_idx_write)
44782147042SKevin Laatz 			fprintf(f, "[wr ptr] ");
44882147042SKevin Laatz 		if (i == idxd->max_batches)
44982147042SKevin Laatz 			fprintf(f, "\n");
45082147042SKevin Laatz 	}
45182147042SKevin Laatz 
45282147042SKevin Laatz 	fprintf(f, "  Curr batch: start = %u, size = %u\n", idxd->batch_start, idxd->batch_size);
45382147042SKevin Laatz 	fprintf(f, "  IDS: avail = %u, returned: %u\n", idxd->ids_avail, idxd->ids_returned);
45482147042SKevin Laatz 	return 0;
45582147042SKevin Laatz }
45682147042SKevin Laatz 
45782147042SKevin Laatz int
458280c3ca0SKevin Laatz idxd_stats_get(const struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
459280c3ca0SKevin Laatz 		struct rte_dma_stats *stats, uint32_t stats_sz)
460280c3ca0SKevin Laatz {
461280c3ca0SKevin Laatz 	struct idxd_dmadev *idxd = dev->fp_obj->dev_private;
462280c3ca0SKevin Laatz 	if (stats_sz < sizeof(*stats))
463280c3ca0SKevin Laatz 		return -EINVAL;
464280c3ca0SKevin Laatz 	*stats = idxd->stats;
465280c3ca0SKevin Laatz 	return 0;
466280c3ca0SKevin Laatz }
467280c3ca0SKevin Laatz 
468280c3ca0SKevin Laatz int
469280c3ca0SKevin Laatz idxd_stats_reset(struct rte_dma_dev *dev, uint16_t vchan __rte_unused)
470280c3ca0SKevin Laatz {
471280c3ca0SKevin Laatz 	struct idxd_dmadev *idxd = dev->fp_obj->dev_private;
472280c3ca0SKevin Laatz 	idxd->stats = (struct rte_dma_stats){0};
473280c3ca0SKevin Laatz 	return 0;
474280c3ca0SKevin Laatz }
475280c3ca0SKevin Laatz 
476280c3ca0SKevin Laatz int
4772f7d42c6SKevin Laatz idxd_info_get(const struct rte_dma_dev *dev, struct rte_dma_info *info, uint32_t size)
4782f7d42c6SKevin Laatz {
4792f7d42c6SKevin Laatz 	struct idxd_dmadev *idxd = dev->fp_obj->dev_private;
4802f7d42c6SKevin Laatz 
4812f7d42c6SKevin Laatz 	if (size < sizeof(*info))
4822f7d42c6SKevin Laatz 		return -EINVAL;
4832f7d42c6SKevin Laatz 
4842f7d42c6SKevin Laatz 	*info = (struct rte_dma_info) {
4852f7d42c6SKevin Laatz 			.dev_capa = RTE_DMA_CAPA_MEM_TO_MEM | RTE_DMA_CAPA_HANDLES_ERRORS |
4862f7d42c6SKevin Laatz 				RTE_DMA_CAPA_OPS_COPY | RTE_DMA_CAPA_OPS_FILL,
4872f7d42c6SKevin Laatz 			.max_vchans = 1,
4882f7d42c6SKevin Laatz 			.max_desc = 4096,
4892f7d42c6SKevin Laatz 			.min_desc = 64,
4902f7d42c6SKevin Laatz 	};
4912f7d42c6SKevin Laatz 	if (idxd->sva_support)
4922f7d42c6SKevin Laatz 		info->dev_capa |= RTE_DMA_CAPA_SVA;
4932f7d42c6SKevin Laatz 	return 0;
4942f7d42c6SKevin Laatz }
4952f7d42c6SKevin Laatz 
4969459de4eSKevin Laatz uint16_t
4979459de4eSKevin Laatz idxd_burst_capacity(const void *dev_private, uint16_t vchan __rte_unused)
4989459de4eSKevin Laatz {
4999459de4eSKevin Laatz 	const struct idxd_dmadev *idxd = dev_private;
5009459de4eSKevin Laatz 	uint16_t write_idx = idxd->batch_start + idxd->batch_size;
5019459de4eSKevin Laatz 	uint16_t used_space;
5029459de4eSKevin Laatz 
5039459de4eSKevin Laatz 	/* Check for space in the batch ring */
5049459de4eSKevin Laatz 	if ((idxd->batch_idx_read == 0 && idxd->batch_idx_write == idxd->max_batches) ||
5059459de4eSKevin Laatz 			idxd->batch_idx_write + 1 == idxd->batch_idx_read)
5069459de4eSKevin Laatz 		return 0;
5079459de4eSKevin Laatz 
50863990aebSBruce Richardson 	/* Subtract and mask to get in correct range */
50963990aebSBruce Richardson 	used_space = (write_idx - idxd->ids_returned) & idxd->desc_ring_mask;
5109459de4eSKevin Laatz 
511a2b43447SBruce Richardson 	const int ret = RTE_MIN((idxd->desc_ring_mask - used_space),
512a2b43447SBruce Richardson 			(idxd->max_batch_size - idxd->batch_size));
513a2b43447SBruce Richardson 	return ret < 0 ? 0 : (uint16_t)ret;
5149459de4eSKevin Laatz }
5159459de4eSKevin Laatz 
5162f7d42c6SKevin Laatz int
5172f7d42c6SKevin Laatz idxd_configure(struct rte_dma_dev *dev __rte_unused, const struct rte_dma_conf *dev_conf,
5182f7d42c6SKevin Laatz 		uint32_t conf_sz)
5192f7d42c6SKevin Laatz {
5202f7d42c6SKevin Laatz 	if (sizeof(struct rte_dma_conf) != conf_sz)
5212f7d42c6SKevin Laatz 		return -EINVAL;
5222f7d42c6SKevin Laatz 
5232f7d42c6SKevin Laatz 	if (dev_conf->nb_vchans != 1)
5242f7d42c6SKevin Laatz 		return -EINVAL;
5252f7d42c6SKevin Laatz 	return 0;
5262f7d42c6SKevin Laatz }
5272f7d42c6SKevin Laatz 
5282f7d42c6SKevin Laatz int
5292f7d42c6SKevin Laatz idxd_vchan_setup(struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
5302f7d42c6SKevin Laatz 		const struct rte_dma_vchan_conf *qconf, uint32_t qconf_sz)
5312f7d42c6SKevin Laatz {
5322f7d42c6SKevin Laatz 	struct idxd_dmadev *idxd = dev->fp_obj->dev_private;
5332f7d42c6SKevin Laatz 	uint16_t max_desc = qconf->nb_desc;
5342f7d42c6SKevin Laatz 
5352f7d42c6SKevin Laatz 	if (sizeof(struct rte_dma_vchan_conf) != qconf_sz)
5362f7d42c6SKevin Laatz 		return -EINVAL;
5372f7d42c6SKevin Laatz 
5382f7d42c6SKevin Laatz 	idxd->qcfg = *qconf;
5392f7d42c6SKevin Laatz 
5402f7d42c6SKevin Laatz 	if (!rte_is_power_of_2(max_desc))
5412f7d42c6SKevin Laatz 		max_desc = rte_align32pow2(max_desc);
5422f7d42c6SKevin Laatz 	IDXD_PMD_DEBUG("DMA dev %u using %u descriptors", dev->data->dev_id, max_desc);
5432f7d42c6SKevin Laatz 	idxd->desc_ring_mask = max_desc - 1;
5442f7d42c6SKevin Laatz 	idxd->qcfg.nb_desc = max_desc;
5452f7d42c6SKevin Laatz 
5462f7d42c6SKevin Laatz 	/* in case we are reconfiguring a device, free any existing memory */
5472f7d42c6SKevin Laatz 	rte_free(idxd->desc_ring);
5482f7d42c6SKevin Laatz 
5492f7d42c6SKevin Laatz 	/* allocate the descriptor ring at 2x size as batches can't wrap */
5502f7d42c6SKevin Laatz 	idxd->desc_ring = rte_zmalloc(NULL, sizeof(*idxd->desc_ring) * max_desc * 2, 0);
5512f7d42c6SKevin Laatz 	if (idxd->desc_ring == NULL)
5522f7d42c6SKevin Laatz 		return -ENOMEM;
5532f7d42c6SKevin Laatz 	idxd->desc_iova = rte_mem_virt2iova(idxd->desc_ring);
5542f7d42c6SKevin Laatz 
5552f7d42c6SKevin Laatz 	idxd->batch_idx_read = 0;
5562f7d42c6SKevin Laatz 	idxd->batch_idx_write = 0;
5572f7d42c6SKevin Laatz 	idxd->batch_start = 0;
5582f7d42c6SKevin Laatz 	idxd->batch_size = 0;
5592f7d42c6SKevin Laatz 	idxd->ids_returned = 0;
5602f7d42c6SKevin Laatz 	idxd->ids_avail = 0;
5612f7d42c6SKevin Laatz 
5622f7d42c6SKevin Laatz 	memset(idxd->batch_comp_ring, 0, sizeof(*idxd->batch_comp_ring) *
5632f7d42c6SKevin Laatz 			(idxd->max_batches + 1));
5642f7d42c6SKevin Laatz 	return 0;
5652f7d42c6SKevin Laatz }
5662f7d42c6SKevin Laatz 
5672f7d42c6SKevin Laatz int
56855dc0f60SKevin Laatz idxd_dmadev_create(const char *name, struct rte_device *dev,
56955dc0f60SKevin Laatz 		   const struct idxd_dmadev *base_idxd,
57055dc0f60SKevin Laatz 		   const struct rte_dma_dev_ops *ops)
57155dc0f60SKevin Laatz {
57255dc0f60SKevin Laatz 	struct idxd_dmadev *idxd = NULL;
57355dc0f60SKevin Laatz 	struct rte_dma_dev *dmadev = NULL;
57455dc0f60SKevin Laatz 	int ret = 0;
57555dc0f60SKevin Laatz 
57682147042SKevin Laatz 	RTE_BUILD_BUG_ON(sizeof(struct idxd_hw_desc) != 64);
57782147042SKevin Laatz 	RTE_BUILD_BUG_ON(offsetof(struct idxd_hw_desc, size) != 32);
57882147042SKevin Laatz 	RTE_BUILD_BUG_ON(sizeof(struct idxd_completion) != 32);
57982147042SKevin Laatz 
58055dc0f60SKevin Laatz 	if (!name) {
58155dc0f60SKevin Laatz 		IDXD_PMD_ERR("Invalid name of the device!");
58255dc0f60SKevin Laatz 		ret = -EINVAL;
58355dc0f60SKevin Laatz 		goto cleanup;
58455dc0f60SKevin Laatz 	}
58555dc0f60SKevin Laatz 
58655dc0f60SKevin Laatz 	/* Allocate device structure */
58755dc0f60SKevin Laatz 	dmadev = rte_dma_pmd_allocate(name, dev->numa_node, sizeof(struct idxd_dmadev));
58855dc0f60SKevin Laatz 	if (dmadev == NULL) {
58955dc0f60SKevin Laatz 		IDXD_PMD_ERR("Unable to allocate dma device");
59055dc0f60SKevin Laatz 		ret = -ENOMEM;
59155dc0f60SKevin Laatz 		goto cleanup;
59255dc0f60SKevin Laatz 	}
59355dc0f60SKevin Laatz 	dmadev->dev_ops = ops;
59455dc0f60SKevin Laatz 	dmadev->device = dev;
59555dc0f60SKevin Laatz 
5963d36a0a1SKevin Laatz 	dmadev->fp_obj->copy = idxd_enqueue_copy;
5973d36a0a1SKevin Laatz 	dmadev->fp_obj->fill = idxd_enqueue_fill;
5983d36a0a1SKevin Laatz 	dmadev->fp_obj->submit = idxd_submit;
59997aeed56SKevin Laatz 	dmadev->fp_obj->completed = idxd_completed;
60097aeed56SKevin Laatz 	dmadev->fp_obj->completed_status = idxd_completed_status;
6019459de4eSKevin Laatz 	dmadev->fp_obj->burst_capacity = idxd_burst_capacity;
6021ea2cdc1SBruce Richardson 	dmadev->fp_obj->dev_private = dmadev->data->dev_private;
6031ea2cdc1SBruce Richardson 
6041ea2cdc1SBruce Richardson 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
6051ea2cdc1SBruce Richardson 		return 0;
6063d36a0a1SKevin Laatz 
60755dc0f60SKevin Laatz 	idxd = dmadev->data->dev_private;
60855dc0f60SKevin Laatz 	*idxd = *base_idxd; /* copy over the main fields already passed in */
60955dc0f60SKevin Laatz 	idxd->dmadev = dmadev;
61055dc0f60SKevin Laatz 
61155dc0f60SKevin Laatz 	/* allocate batch index ring and completion ring.
61255dc0f60SKevin Laatz 	 * The +1 is because we can never fully use
61355dc0f60SKevin Laatz 	 * the ring, otherwise read == write means both full and empty.
61455dc0f60SKevin Laatz 	 */
61555dc0f60SKevin Laatz 	idxd->batch_comp_ring = rte_zmalloc_socket(NULL, (sizeof(idxd->batch_idx_ring[0]) +
61655dc0f60SKevin Laatz 			sizeof(idxd->batch_comp_ring[0]))	* (idxd->max_batches + 1),
61755dc0f60SKevin Laatz 			sizeof(idxd->batch_comp_ring[0]), dev->numa_node);
61855dc0f60SKevin Laatz 	if (idxd->batch_comp_ring == NULL) {
619*f665790aSDavid Marchand 		IDXD_PMD_ERR("Unable to reserve memory for batch data");
62055dc0f60SKevin Laatz 		ret = -ENOMEM;
62155dc0f60SKevin Laatz 		goto cleanup;
62255dc0f60SKevin Laatz 	}
62355dc0f60SKevin Laatz 	idxd->batch_idx_ring = (void *)&idxd->batch_comp_ring[idxd->max_batches+1];
62455dc0f60SKevin Laatz 	idxd->batch_iova = rte_mem_virt2iova(idxd->batch_comp_ring);
62555dc0f60SKevin Laatz 
62655dc0f60SKevin Laatz 	idxd->dmadev->state = RTE_DMA_DEV_READY;
62755dc0f60SKevin Laatz 
62855dc0f60SKevin Laatz 	return 0;
62955dc0f60SKevin Laatz 
63055dc0f60SKevin Laatz cleanup:
63155dc0f60SKevin Laatz 	if (dmadev)
63255dc0f60SKevin Laatz 		rte_dma_pmd_release(name);
63355dc0f60SKevin Laatz 
63455dc0f60SKevin Laatz 	return ret;
63555dc0f60SKevin Laatz }
63655dc0f60SKevin Laatz 
637e33ad06eSKevin Laatz int idxd_pmd_logtype;
638e33ad06eSKevin Laatz 
639e33ad06eSKevin Laatz RTE_LOG_REGISTER_DEFAULT(idxd_pmd_logtype, WARNING);
640