[Accel-config] Re: [PATCH v1 5/5] accel-config/test: add -n to input the submit descriptor number

Ramesh Thomas <ramesh.thomas at intel.com> Fri, 03 Dec 2021 16:05:52 -0800
Newsgroups dev.linux.lists.accel-config
Message-ID <[email protected]>
On 11/30/2021 2:51 AM, Tony Zhu wrote:
> It is necessary to input the submit descriptor number for tests.
> A large number of submissions may trigger abnormal issues.
> 
> Signed-off-by: Tony Zhu <tony.zhu(a)intel.com>
> ---
>   test/dsa.c      | 375 ++++++++++++++++++++++++++++++++-------------
>   test/dsa.h      |  56 ++++---
>   test/dsa_test.c | 392 +++++++++++++++++++++++++++++-------------------
>   test/prep.c     |  30 ++--
>   4 files changed, 556 insertions(+), 297 deletions(-)
> 
> diff --git a/test/dsa.c b/test/dsa.c
> index 1191254..83e2b4d 100644
> --- a/test/dsa.c
> +++ b/test/dsa.c
> @@ -215,6 +215,7 @@ int dsa_alloc(struct dsa_context *ctx, int shared, int dev_id, int wq_id)
>   	dev = accfg_wq_get_device(ctx->wq);
>   	ctx->dedicated = accfg_wq_get_mode(ctx->wq);
>   	ctx->wq_size = accfg_wq_get_size(ctx->wq);
> +	ctx->threshold = accfg_wq_get_threshold(ctx->wq);
>   	ctx->wq_idx = accfg_wq_get_id(ctx->wq);
>   	ctx->bof = accfg_wq_get_block_on_fault(ctx->wq);
>   	ctx->wq_max_batch_size = accfg_wq_get_max_batch_size(ctx->wq);
> @@ -232,15 +233,23 @@ int dsa_alloc(struct dsa_context *ctx, int shared, int dev_id, int wq_id)
>   	return 0;
>   }
>   
> -int alloc_task(struct dsa_context *ctx)
> +int alloc_multiple_tasks(struct dsa_context *ctx, int num_itr)
>   {
> -	ctx->single_task = __alloc_task();
> -	if (!ctx->single_task)
> -		return -ENOMEM;
> +	struct task_node *tmp_tsk_node;
> +	int cnt = 0;
>   
> -	dbg("single task allocated, desc %#lx comp %#lx\n",
> -	    ctx->single_task->desc, ctx->single_task->comp);
> +	while (cnt < num_itr) {
> +		tmp_tsk_node = ctx->multi_task_node;
> +		ctx->multi_task_node = (struct task_node *)malloc(sizeof(struct task_node *));
> +		if (!ctx->multi_task_node)
> +			return -ENOMEM;
>   
> +		ctx->multi_task_node->tsk = __alloc_task();
> +		if (!ctx->multi_task_node->tsk)
> +			return -ENOMEM;
> +		ctx->multi_task_node->next = tmp_tsk_node;
> +		cnt++;
> +	}
>   	return DSA_STATUS_OK;
>   }
>   
> @@ -333,48 +342,60 @@ int init_task(struct task *tsk, int tflags, int opcode,
>   	return DSA_STATUS_OK;
>   }
>   
> -int alloc_batch_task(struct dsa_context *ctx, unsigned int task_num)
> +int alloc_batch_task(struct dsa_context *ctx, unsigned int task_num, unsigned long num_itr)

This gives compilation error

dsa.c:345:5: error: conflicting types for ‘alloc_batch_task’; have 
‘int(struct dsa_context *, unsigned int,  long unsigned int)’
   345 | int alloc_batch_task(struct dsa_context *ctx, unsigned int 
task_num, unsigned long num_itr)
       |     ^~~~~~~~~~~~~~~~
In file included from dsa.c:17:
dsa.h:272:5: note: previous declaration of ‘alloc_batch_task’ with type 
‘int(struct dsa_context *, unsigned int,  int)’
   272 | int alloc_batch_task(struct dsa_context *ctx, unsigned int 
task_num, int num_itr);
       |     ^~~~~~~~~~~~~~~~


More compilation errors. Not sure how you were able to test this with 
these build errors.

dsa.c: In function ‘alloc_multiple_tasks’:
dsa.c:247:37: warning: array subscript ‘struct task_node[0]’ is partly 
outside array bounds of ‘unsigned char[8]’ [-Warray-bounds]
   247 |                 ctx->multi_task_node->tsk = __alloc_task();
       |                                     ^~
dsa.c:243:60: note: object of size 8 allocated by ‘malloc’
   243 |                 ctx->multi_task_node = (struct task_node 
*)malloc(sizeof(struct task_node *));
       | 
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dsa.c: In function ‘alloc_batch_task’:
dsa.c:364:38: warning: array subscript ‘struct btask_node[0]’ is partly 
outside array bounds of ‘unsigned char[8]’ [-Warray-bounds]
   364 |                 ctx->multi_btask_node->btsk = 
malloc(sizeof(struct batch_task));
       |                                      ^~
dsa.c:360:25: note: object of size 8 allocated by ‘malloc’
   360 |                         malloc(sizeof(struct btask_node *));
       |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   CCLD     dsa_test


>   {
> +	struct btask_node *btsk_node;
>   	struct batch_task *btsk;
> +	unsigned long cnt = 0;
>   
>   	if (!ctx->is_batch) {
>   		err("%s is valid only if 'is_batch' is enabled", __func__);
>   		return -EINVAL;
>   	}
>   
> -	ctx->batch_task = malloc(sizeof(struct batch_task));
> -	if (!ctx->batch_task)
> -		return -ENOMEM;
> -	memset(ctx->batch_task, 0, sizeof(struct batch_task));
> +	while (cnt < num_itr) {
> +		btsk_node = ctx->multi_btask_node;
>   
> -	btsk = ctx->batch_task;
> +		ctx->multi_btask_node = (struct btask_node *)
> +			malloc(sizeof(struct btask_node *));
> +		if (!ctx->multi_btask_node)
> +			return -ENOMEM;
>   
> -	btsk->core_task = __alloc_task();
> -	if (!btsk->core_task)
> -		return -ENOMEM;
> +		ctx->multi_btask_node->btsk = malloc(sizeof(struct batch_task));
> +		if (!ctx->multi_btask_node->btsk)
> +			return -ENOMEM;
> +		memset(ctx->multi_btask_node->btsk, 0, sizeof(struct batch_task));
> +
> +		btsk = ctx->multi_btask_node->btsk;
>   
> -	btsk->sub_tasks = malloc(task_num * sizeof(struct task));
> -	if (!btsk->sub_tasks)
> -		return -ENOMEM;
> -	memset(btsk->sub_tasks, 0, task_num * sizeof(struct task));
> +		btsk->core_task = __alloc_task();
> +		if (!btsk->core_task)
> +			return -ENOMEM;
>   
> -	btsk->sub_descs = aligned_alloc(64,
> -					task_num * sizeof(struct dsa_hw_desc));
> -	if (!btsk->sub_descs)
> -		return -ENOMEM;
> -	memset(btsk->sub_descs, 0, task_num * sizeof(struct dsa_hw_desc));
> +		btsk->sub_tasks = malloc(task_num * sizeof(struct task));
> +		if (!btsk->sub_tasks)
> +			return -ENOMEM;
> +		memset(btsk->sub_tasks, 0, task_num * sizeof(struct task));
>   
> -	btsk->sub_comps = aligned_alloc(32,
> -					task_num * sizeof(struct dsa_completion_record));
> -	if (!btsk->sub_comps)
> -		return -ENOMEM;
> -	memset(btsk->sub_comps, 0,
> -	       task_num * sizeof(struct dsa_completion_record));
> +		btsk->sub_descs = aligned_alloc(64, task_num * sizeof(struct dsa_hw_desc));
> +		if (!btsk->sub_descs)
> +			return -ENOMEM;
> +		memset(btsk->sub_descs, 0, task_num * sizeof(struct dsa_hw_desc));
>   
> -	dbg("batch task allocated %#lx, ctask %#lx, sub_tasks %#lx\n",
> -	    btsk, btsk->core_task, btsk->sub_tasks);
> -	dbg("sub_descs %#lx, sub_comps %#lx\n",
> -	    btsk->sub_descs, btsk->sub_comps);
> +		btsk->sub_comps =
> +			aligned_alloc(32, task_num * sizeof(struct dsa_completion_record));
> +		if (!btsk->sub_comps)
> +			return -ENOMEM;
> +		memset(btsk->sub_comps, 0,
> +		       task_num * sizeof(struct dsa_completion_record));
> +
> +		dbg("batch task allocated %#lx, ctask %#lx, sub_tasks %#lx\n",
> +		    btsk, btsk->core_task, btsk->sub_tasks);
> +		dbg("sub_descs %#lx, sub_comps %#lx\n",
> +		    btsk->sub_descs, btsk->sub_comps);
> +		ctx->multi_btask_node->next = btsk_node;
> +		cnt++;
> +	}
>   
>   	return DSA_STATUS_OK;
>   }
> @@ -541,10 +562,31 @@ void dsa_free(struct dsa_context *ctx)
>   
>   void dsa_free_task(struct dsa_context *ctx)
>   {
> -	if (!ctx->is_batch)
> -		free_task(ctx->single_task);
> -	else
> -		free_batch_task(ctx->batch_task);
> +	if (!ctx->is_batch) {
> +		struct task_node *tsk_node = NULL, *tmp_node = NULL;
> +
> +		tsk_node = ctx->multi_task_node;
> +		while (tsk_node) {
> +			tmp_node = tsk_node->next;
> +			free_task(tsk_node->tsk);
> +			tsk_node->tsk = NULL;
> +			free(tsk_node);
> +			tsk_node = tmp_node;
> +		}
> +		ctx->multi_task_node = NULL;
> +	} else {
> +		struct btask_node *tsk_node = NULL, *tmp_node = NULL;
> +
> +		tsk_node = ctx->multi_btask_node;
> +		while (tsk_node) {
> +			tmp_node = tsk_node->next;
> +			free_batch_task(tsk_node->btsk);
> +			tsk_node->btsk = NULL;
> +			free(tsk_node);
> +			tsk_node = tmp_node;
> +		}
> +		ctx->multi_task_node = NULL;
> +	}
>   }
>   
>   void free_task(struct task *tsk)
> @@ -594,9 +636,9 @@ void free_batch_task(struct batch_task *btsk)
>   	free(btsk);
>   }
>   
> -int dsa_wait_noop(struct dsa_context *ctx)
> +int dsa_wait_noop(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_completion_record *comp = ctx->single_task->comp;
> +	struct dsa_completion_record *comp = tsk->comp;
>   	int rc;
>   
>   	rc = dsa_wait_on_desc_timeout(comp, ms_timeout);
> @@ -608,25 +650,40 @@ int dsa_wait_noop(struct dsa_context *ctx)
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_noop(struct dsa_context *ctx)
> +int dsa_noop_multi_task_nodes(struct dsa_context *ctx)
>   {
> -	struct task *tsk = ctx->single_task;
> +	struct task_node *tsk_node = ctx->multi_task_node;
>   	int ret = DSA_STATUS_OK;
>   
> -	tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +	while (tsk_node) {
> +		tsk_node->tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
>   
> -	dsa_prep_noop(tsk);
> -	dsa_desc_submit(ctx, tsk->desc);
> -	ret = dsa_wait_noop(ctx);
> +		dsa_prep_noop(tsk_node->tsk);
> +		tsk_node = tsk_node->next;
> +	}
>   
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		dsa_desc_submit(ctx, tsk_node->tsk->desc);
> +		tsk_node = tsk_node->next;
> +	}
> +	tsk_node = ctx->multi_task_node;
> +	info("Submitted all noop jobs\n");
> +
> +	while (tsk_node) {
> +		ret = dsa_wait_noop(ctx, tsk_node->tsk);
> +		if (ret != DSA_STATUS_OK)
> +			info("Desc: %p failed with ret: %d\n",
> +			     tsk_node->tsk->desc, tsk_node->tsk->comp->status);
> +		tsk_node = tsk_node->next;
> +	}
>   	return ret;
>   }
>   
> -int dsa_wait_batch(struct dsa_context *ctx)
> +int dsa_wait_batch(struct batch_task *btsk)
>   {
>   	int rc;
>   
> -	struct batch_task *btsk = ctx->batch_task;
>   	struct task *ctsk = btsk->core_task;
>   
>   	info("wait batch\n");
> @@ -641,10 +698,10 @@ int dsa_wait_batch(struct dsa_context *ctx)
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_wait_memcpy(struct dsa_context *ctx)
> +int dsa_wait_memcpy(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_hw_desc *desc = ctx->single_task->desc;
> -	struct dsa_completion_record *comp = ctx->single_task->comp;
> +	struct dsa_hw_desc *desc = tsk->desc;
> +	struct dsa_completion_record *comp = tsk->comp;
>   	int rc;
>   
>   again:
> @@ -657,33 +714,50 @@ again:
>   	/* re-submit if PAGE_FAULT reported by HW && BOF is off */
>   	if (stat_val(comp->status) == DSA_COMP_PAGE_FAULT_NOBOF &&
>   	    !(desc->flags & IDXD_OP_FLAG_BOF)) {
> -		dsa_reprep_memcpy(ctx);
> +		dsa_reprep_memcpy(ctx, tsk);
>   		goto again;
>   	}
>   
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_memcpy(struct dsa_context *ctx)
> +int dsa_memcpy_multi_task_nodes(struct dsa_context *ctx)
>   {
> -	struct task *tsk = ctx->single_task;
> +	struct task_node *tsk_node = ctx->multi_task_node;
>   	int ret = DSA_STATUS_OK;
>   
> -	tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> -	if ((tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> -		tsk->dflags |= IDXD_OP_FLAG_BOF;
> +	while (tsk_node) {
> +		tsk_node->tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +		if ((tsk_node->tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> +			tsk_node->tsk->dflags |= IDXD_OP_FLAG_BOF;
>   
> -	dsa_prep_memcpy(tsk);
> -	dsa_desc_submit(ctx, tsk->desc);
> -	ret = dsa_wait_memcpy(ctx);
> +		dsa_prep_memcpy(tsk_node->tsk);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	info("Submitted all memcpy jobs\n");
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		dsa_desc_submit(ctx, tsk_node->tsk->desc);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		ret = dsa_wait_memcpy(ctx, tsk_node->tsk);
> +		if (ret != DSA_STATUS_OK)
> +			info("Desc: %p failed with ret: %d\n",
> +			     tsk_node->tsk->desc, tsk_node->tsk->comp->status);
> +		tsk_node = tsk_node->next;
> +	}
>   
>   	return ret;
>   }
>   
> -int dsa_wait_memfill(struct dsa_context *ctx)
> +int dsa_wait_memfill(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_hw_desc *desc = ctx->single_task->desc;
> -	struct dsa_completion_record *comp = ctx->single_task->comp;
> +	struct dsa_hw_desc *desc = tsk->desc;
> +	struct dsa_completion_record *comp = tsk->comp;
>   	int rc;
>   
>   again:
> @@ -697,33 +771,50 @@ again:
>   	/* re-submit if PAGE_FAULT reported by HW && BOF is off */
>   	if (stat_val(comp->status) == DSA_COMP_PAGE_FAULT_NOBOF &&
>   	    !(desc->flags & IDXD_OP_FLAG_BOF)) {
> -		dsa_reprep_memfill(ctx);
> +		dsa_reprep_memfill(ctx, tsk);
>   		goto again;
>   	}
>   
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_memfill(struct dsa_context *ctx)
> +int dsa_memfill_multi_task_nodes(struct dsa_context *ctx)
>   {
> -	struct task *tsk = ctx->single_task;
> +	struct task_node *tsk_node = ctx->multi_task_node;
>   	int ret = DSA_STATUS_OK;
>   
> -	tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> -	if ((tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> -		tsk->dflags |= IDXD_OP_FLAG_BOF;
> +	while (tsk_node) {
> +		tsk_node->tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +		if ((tsk_node->tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> +			tsk_node->tsk->dflags |= IDXD_OP_FLAG_BOF;
> +
> +		dsa_prep_memfill(tsk_node->tsk);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	info("Submitted all memcpy jobs\n");
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		dsa_desc_submit(ctx, tsk_node->tsk->desc);
> +		tsk_node = tsk_node->next;
> +	}
>   
> -	dsa_prep_memfill(tsk);
> -	dsa_desc_submit(ctx, tsk->desc);
> -	ret = dsa_wait_memfill(ctx);
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		ret = dsa_wait_memfill(ctx, tsk_node->tsk);
> +		if (ret != DSA_STATUS_OK)
> +			info("Desc: %p failed with ret: %d\n",
> +			     tsk_node->tsk->desc, tsk_node->tsk->comp->status);
> +		tsk_node = tsk_node->next;
> +	}
>   
>   	return ret;
>   }
>   
> -int dsa_wait_compare(struct dsa_context *ctx)
> +int dsa_wait_compare(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_hw_desc *desc = ctx->single_task->desc;
> -	struct dsa_completion_record *comp = ctx->single_task->comp;
> +	struct dsa_hw_desc *desc = tsk->desc;
> +	struct dsa_completion_record *comp = tsk->comp;
>   	int rc;
>   
>   again:
> @@ -737,33 +828,50 @@ again:
>   	/* re-submit if PAGE_FAULT reported by HW && BOF is off */
>   	if (stat_val(comp->status) == DSA_COMP_PAGE_FAULT_NOBOF &&
>   	    !(desc->flags & IDXD_OP_FLAG_BOF)) {
> -		dsa_reprep_compare(ctx);
> +		dsa_reprep_compare(ctx, tsk);
>   		goto again;
>   	}
>   
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_compare(struct dsa_context *ctx)
> +int dsa_compare_multi_task_nodes(struct dsa_context *ctx)
>   {
> -	struct task *tsk = ctx->single_task;
> +	struct task_node *tsk_node = ctx->multi_task_node;
>   	int ret = DSA_STATUS_OK;
>   
> -	tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> -	if ((tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> -		tsk->dflags |= IDXD_OP_FLAG_BOF;
> +	while (tsk_node) {
> +		tsk_node->tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +		if ((tsk_node->tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> +			tsk_node->tsk->dflags |= IDXD_OP_FLAG_BOF;
> +
> +		dsa_prep_compare(tsk_node->tsk);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	info("Submitted all memcpy jobs\n");
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		dsa_desc_submit(ctx, tsk_node->tsk->desc);
> +		tsk_node = tsk_node->next;
> +	}
>   
> -	dsa_prep_compare(tsk);
> -	dsa_desc_submit(ctx, tsk->desc);
> -	ret = dsa_wait_compare(ctx);
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		ret = dsa_wait_compare(ctx, tsk_node->tsk);
> +		if (ret != DSA_STATUS_OK)
> +			info("Desc: %p failed with ret: %d\n",
> +			     tsk_node->tsk->desc, tsk_node->tsk->comp->status);
> +		tsk_node = tsk_node->next;
> +	}
>   
>   	return ret;
>   }
>   
> -int dsa_wait_compval(struct dsa_context *ctx)
> +int dsa_wait_compval(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_hw_desc *desc = ctx->single_task->desc;
> -	struct dsa_completion_record *comp = ctx->single_task->comp;
> +	struct dsa_hw_desc *desc = tsk->desc;
> +	struct dsa_completion_record *comp = tsk->comp;
>   	int rc;
>   
>   again:
> @@ -777,33 +885,50 @@ again:
>   	/* re-submit if PAGE_FAULT reported by HW && BOF is off */
>   	if (stat_val(comp->status) == DSA_COMP_PAGE_FAULT_NOBOF &&
>   	    !(desc->flags & IDXD_OP_FLAG_BOF)) {
> -		dsa_reprep_compval(ctx);
> +		dsa_reprep_compval(ctx, tsk);
>   		goto again;
>   	}
>   
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_compval(struct dsa_context *ctx)
> +int dsa_compval_multi_task_nodes(struct dsa_context *ctx)
>   {
> -	struct task *tsk = ctx->single_task;
> +	struct task_node *tsk_node = ctx->multi_task_node;
>   	int ret = DSA_STATUS_OK;
>   
> -	tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> -	if ((tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> -		tsk->dflags |= IDXD_OP_FLAG_BOF;
> +	while (tsk_node) {
> +		tsk_node->tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +		if ((tsk_node->tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> +			tsk_node->tsk->dflags |= IDXD_OP_FLAG_BOF;
> +
> +		dsa_prep_compval(tsk_node->tsk);
> +		tsk_node = tsk_node->next;
> +	}
>   
> -	dsa_prep_compval(tsk);
> -	dsa_desc_submit(ctx, tsk->desc);
> -	ret = dsa_wait_compval(ctx);
> +	info("Submitted all memcpy jobs\n");
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		dsa_desc_submit(ctx, tsk_node->tsk->desc);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		ret = dsa_wait_compval(ctx, tsk_node->tsk);
> +		if (ret != DSA_STATUS_OK)
> +			info("Desc: %p failed with ret: %d\n",
> +			     tsk_node->tsk->desc, tsk_node->tsk->comp->status);
> +		tsk_node = tsk_node->next;
> +	}
>   
>   	return ret;
>   }
>   
> -int dsa_wait_dualcast(struct dsa_context *ctx)
> +int dsa_wait_dualcast(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_hw_desc *desc = ctx->single_task->desc;
> -	struct dsa_completion_record *comp = ctx->single_task->comp;
> +	struct dsa_hw_desc *desc = tsk->desc;
> +	struct dsa_completion_record *comp = tsk->comp;
>   	int rc;
>   
>   again:
> @@ -816,25 +941,42 @@ again:
>   	/* re-submit if PAGE_FAULT reported by HW && BOF is off */
>   	if (stat_val(comp->status) == DSA_COMP_PAGE_FAULT_NOBOF &&
>   	    !(desc->flags & IDXD_OP_FLAG_BOF)) {
> -		dsa_reprep_dualcast(ctx);
> +		dsa_reprep_dualcast(ctx, tsk);
>   		goto again;
>   	}
>   
>   	return DSA_STATUS_OK;
>   }
>   
> -int dsa_dualcast(struct dsa_context *ctx)
> +int dsa_dualcast_multi_task_nodes(struct dsa_context *ctx)
>   {
> -	struct task *tsk = ctx->single_task;
> +	struct task_node *tsk_node = ctx->multi_task_node;
>   	int ret = DSA_STATUS_OK;
>   
> -	tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> -	if ((tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> -		tsk->dflags |= IDXD_OP_FLAG_BOF;
> +	while (tsk_node) {
> +		tsk_node->tsk->dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +		if ((tsk_node->tsk->test_flags & TEST_FLAGS_BOF) && ctx->bof)
> +			tsk_node->tsk->dflags |= IDXD_OP_FLAG_BOF;
>   
> -	dsa_prep_dualcast(tsk);
> -	dsa_desc_submit(ctx, tsk->desc);
> -	ret = dsa_wait_dualcast(ctx);
> +		dsa_prep_dualcast(tsk_node->tsk);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	info("Submitted all memcpy jobs\n");
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		dsa_desc_submit(ctx, tsk_node->tsk->desc);
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	tsk_node = ctx->multi_task_node;
> +	while (tsk_node) {
> +		ret = dsa_wait_dualcast(ctx, tsk_node->tsk);
> +		if (ret != DSA_STATUS_OK)
> +			info("Desc: %p failed with ret: %d\n",
> +			     tsk_node->tsk->desc, tsk_node->tsk->comp->status);
> +		tsk_node = tsk_node->next;
> +	}
>   
>   	return ret;
>   }
> @@ -872,6 +1014,23 @@ int task_result_verify(struct task *tsk, int mismatch_expected)
>   	return DSA_STATUS_OK;
>   }
>   
> +int task_result_verify_task_nodes(struct dsa_context *ctx, int mismatch_expected)
> +{
> +	struct task_node *tsk_node = ctx->multi_task_node;
> +	int ret = DSA_STATUS_OK;
> +
> +	while (tsk_node) {
> +		ret = task_result_verify(tsk_node->tsk, mismatch_expected);
> +		if (ret != DSA_STATUS_OK) {
> +			err("memory result verify failed %d\n", ret);
> +			return ret;
> +		}
> +		tsk_node = tsk_node->next;
> +	}
> +
> +	return ret;
> +}
> +
>   int task_result_verify_memcpy(struct task *tsk, int mismatch_expected)
>   {
>   	int rc;
> diff --git a/test/dsa.h b/test/dsa.h
> index df2fc09..382fc66 100644
> --- a/test/dsa.h
> +++ b/test/dsa.h
> @@ -62,6 +62,11 @@ struct task {
>   	int test_flags;
>   };
>   
> +struct task_node {
> +	struct task *tsk;
> +	struct task_node *next;
> +};
> +
>   /* metadata for batch DSA task */
>   struct batch_task {
>   	struct task *core_task;     /* core task with batch opcode 0x1*/
> @@ -72,6 +77,11 @@ struct batch_task {
>   	int test_flags;
>   };
>   
> +struct btask_node {
> +	struct batch_task *btsk;
> +	struct btask_node *next;
> +};
> +
>   struct dsa_context {
>   	struct accfg_ctx *ctx;
>   	struct accfg_wq *wq;
> @@ -84,6 +94,7 @@ struct dsa_context {
>   	int wq_idx;
>   	void *wq_reg;
>   	int wq_size;
> +	int threshold;
>   	int dedicated;
>   	int bof;
>   	unsigned int wq_max_batch_size;
> @@ -92,8 +103,8 @@ struct dsa_context {
>   
>   	int is_batch;
>   	union {
> -		struct task *single_task;
> -		struct batch_task *batch_task;
> +		struct task_node *multi_task_node;
> +		struct btask_node *multi_btask_node;
>   	};
>   };
>   
> @@ -214,42 +225,43 @@ int dsa_enqcmd(struct dsa_context *ctx, struct dsa_hw_desc *hw);
>   
>   struct dsa_context *dsa_init(void);
>   int dsa_alloc(struct dsa_context *ctx, int shared, int dev_id, int wq_id);
> -int alloc_task(struct dsa_context *ctx);
> +int alloc_multiple_tasks(struct dsa_context *ctx, int num_itr);
>   struct task *__alloc_task(void);
>   int init_task(struct task *tsk, int tflags, int opcode,
>   	      unsigned long xfer_size);
>   
> -int dsa_noop(struct dsa_context *ctx);
> -int dsa_wait_noop(struct dsa_context *ctx);
> +int dsa_noop_multi_task_nodes(struct dsa_context *ctx);
> +int dsa_wait_noop(struct dsa_context *ctx, struct task *tsk);
>   
> -int dsa_memcpy(struct dsa_context *ctx);
> -int dsa_wait_memcpy(struct dsa_context *ctx);
> +int dsa_memcpy_multi_task_nodes(struct dsa_context *ctx);
> +int dsa_wait_memcpy(struct dsa_context *ctx, struct task *tsk);
>   
> -int dsa_memfill(struct dsa_context *ctx);
> -int dsa_wait_memfill(struct dsa_context *ctx);
> +int dsa_memfill_multi_task_nodes(struct dsa_context *ctx);
> +int dsa_wait_memfill(struct dsa_context *ctx, struct task *tsk);
>   
> -int dsa_compare(struct dsa_context *ctx);
> -int dsa_wait_compare(struct dsa_context *ctx);
> +int dsa_compare_multi_task_nodes(struct dsa_context *ctx);
> +int dsa_wait_compare(struct dsa_context *ctx, struct task *tsk);
>   
> -int dsa_compval(struct dsa_context *ctx);
> -int dsa_wait_compval(struct dsa_context *ctx);
> +int dsa_compval_multi_task_nodes(struct dsa_context *ctx);
> +int dsa_wait_compval(struct dsa_context *ctx, struct task *tsk);
>   
> -int dsa_dualcast(struct dsa_context *ctx);
> -int dsa_wait_dualcast(struct dsa_context *ctx);
> +int dsa_dualcast_multi_task_nodes(struct dsa_context *ctx);
> +int dsa_wait_dualcast(struct dsa_context *ctx, struct task *tsk);
>   
>   void dsa_prep_noop(struct task *tsk);
>   void dsa_prep_memcpy(struct task *tsk);
> -void dsa_reprep_memcpy(struct dsa_context *ctx);
> +void dsa_reprep_memcpy(struct dsa_context *ctx, struct task *tsk);
>   void dsa_prep_memfill(struct task *tsk);
> -void dsa_reprep_memfill(struct dsa_context *ctx);
> +void dsa_reprep_memfill(struct dsa_context *ctx, struct task *tsk);
>   void dsa_prep_compare(struct task *tsk);
> -void dsa_reprep_compare(struct dsa_context *ctx);
> +void dsa_reprep_compare(struct dsa_context *ctx, struct task *tsk);
>   void dsa_prep_compval(struct task *tsk);
> -void dsa_reprep_compval(struct dsa_context *ctx);
> +void dsa_reprep_compval(struct dsa_context *ctx, struct task *tsk);
>   void dsa_prep_dualcast(struct task *tsk);
> -void dsa_reprep_dualcast(struct dsa_context *ctx);
> +void dsa_reprep_dualcast(struct dsa_context *ctx, struct task *tsk);
>   
>   int task_result_verify(struct task *tsk, int mismatch_expected);
> +int task_result_verify_task_nodes(struct dsa_context *ctx, int mismatch_expected);
>   int task_result_verify_memcpy(struct task *tsk, int mismatch_expected);
>   int task_result_verify_memfill(struct task *tsk, int mismatch_expected);
>   int task_result_verify_compare(struct task *tsk, int mismatch_expected);
> @@ -257,7 +269,7 @@ int task_result_verify_compval(struct task *tsk, int mismatch_expected);
>   int task_result_verify_dualcast(struct task *tsk, int mismatch_expected);
>   int batch_result_verify(struct batch_task *btsk, int bof);
>   
> -int alloc_batch_task(struct dsa_context *ctx, unsigned int task_num);
> +int alloc_batch_task(struct dsa_context *ctx, unsigned int task_num, int num_itr);
>   int init_batch_task(struct batch_task *btsk, int task_num, int tflags,
>   		    int opcode, unsigned long xfer_size, unsigned long dflags);
>   
> @@ -268,7 +280,7 @@ void dsa_prep_batch_memfill(struct batch_task *btsk);
>   void dsa_prep_batch_compare(struct batch_task *btsk);
>   void dsa_prep_batch_compval(struct batch_task *btsk);
>   void dsa_prep_batch_dualcast(struct batch_task *btsk);
> -int dsa_wait_batch(struct dsa_context *ctx);
> +int dsa_wait_batch(struct batch_task *btsk);
>   
>   void dsa_free(struct dsa_context *ctx);
>   void dsa_free_task(struct dsa_context *ctx);
> diff --git a/test/dsa_test.c b/test/dsa_test.c
> index cd3bd50..eccf89c 100644
> --- a/test/dsa_test.c
> +++ b/test/dsa_test.c
> @@ -24,19 +24,22 @@ static void usage(void)
>   	"-b <opcode> ; if batch opcode, opcode in the batch\n"
>   	"-c <batch_size> ; if batch opcode, number of descriptors for batch\n"
>   	"-d              ; wq device such as dsa0/wq0.0\n"
> +	"-n <number of descriptors> ;descriptor count to submit\n"
>   	"-t <ms timeout> ; ms to wait for descs to complete\n"
>   	"-v              ; verbose\n"
>   	"-h              ; print this message\n");
>   }
>   
>   static int test_batch(struct dsa_context *ctx, size_t buf_size,
> -		      int tflags, uint32_t bopcode, unsigned int bsize)
> +		      int tflags, uint32_t bopcode, unsigned int bsize, int num_desc)
>   {
> +	struct btask_node *btsk_node;
>   	unsigned long dflags;
> -	int rc = 0;
> +	int rc = DSA_STATUS_OK;
> +	int itr = num_desc, i = 0, range = 0;
>   
> -	info("batch: len %#lx tflags %#x bopcode %#x batch_no %d\n",
> -	     buf_size, tflags, bopcode, bsize);
> +	info("batch: len %#lx tflags %#x bopcode %#x batch_no %d num_desc %ld\n",
> +	     buf_size, tflags, bopcode, bsize, num_desc);
>   
>   	if (bopcode == DSA_OPCODE_BATCH) {
>   		err("Can't have batch op inside batch op\n");
> @@ -45,198 +48,279 @@ static int test_batch(struct dsa_context *ctx, size_t buf_size,
>   
>   	ctx->is_batch = 1;
>   
> -	rc = alloc_batch_task(ctx, bsize);
> -	if (rc != DSA_STATUS_OK)
> -		return rc;
> +	if (ctx->dedicated == ACCFG_WQ_SHARED)
> +		range = ctx->threshold;
> +	else
> +		range = ctx->wq_size - 1;
>   
> -	dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> -	if ((tflags & TEST_FLAGS_BOF) && ctx->bof)
> -		dflags |= IDXD_OP_FLAG_BOF;
> +	while (itr > 0 && rc == DSA_STATUS_OK) {
> +		i = (itr < range) ? itr : range;
> +		rc = alloc_batch_task(ctx, bsize, i);
> +		if (rc != DSA_STATUS_OK)
> +			return rc;
>   
> -	rc = init_batch_task(ctx->batch_task, bsize, tflags, bopcode,
> -			     buf_size, dflags);
> -	if (rc != DSA_STATUS_OK)
> -		return rc;
> +		dflags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR;
> +		if ((tflags & TEST_FLAGS_BOF) && ctx->bof)
> +			dflags |= IDXD_OP_FLAG_BOF;
> +
> +		/* allocate memory to src and dest buffers and fill in the desc for all the nodes*/
> +		btsk_node = ctx->multi_btask_node;
> +		while (btsk_node) {
> +			rc = init_batch_task(btsk_node->btsk, bsize, tflags, bopcode,
> +					     buf_size, dflags);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +
> +			switch (bopcode) {
> +			case DSA_OPCODE_NOOP:
> +				dsa_prep_batch_noop(btsk_node->btsk);
> +				break;
> +
> +			case DSA_OPCODE_MEMMOVE:
> +				dsa_prep_batch_memcpy(btsk_node->btsk);
> +				break;
> +
> +			case DSA_OPCODE_MEMFILL:
> +				dsa_prep_batch_memfill(btsk_node->btsk);
> +				break;
> +
> +			case DSA_OPCODE_COMPARE:
> +				dsa_prep_batch_compare(btsk_node->btsk);
> +				break;
> +			case DSA_OPCODE_COMPVAL:
> +				dsa_prep_batch_compval(btsk_node->btsk);
> +				break;
> +
> +			case DSA_OPCODE_DUALCAST:
> +				dsa_prep_batch_dualcast(btsk_node->btsk);
> +				break;
> +			default:
> +				err("Unsupported op %#x\n", bopcode);
> +				return -EINVAL;
> +			}
>   
> -	switch (bopcode) {
> -	case DSA_OPCODE_NOOP:
> -		dsa_prep_batch_noop(ctx->batch_task);
> -		break;
> -	case DSA_OPCODE_MEMMOVE:
> -		dsa_prep_batch_memcpy(ctx->batch_task);
> -		break;
> +			btsk_node = btsk_node->next;
> +		}
>   
> -	case DSA_OPCODE_MEMFILL:
> -		dsa_prep_batch_memfill(ctx->batch_task);
> -		break;
> +		btsk_node = ctx->multi_btask_node;
> +		while (btsk_node) {
> +			dsa_prep_batch(btsk_node->btsk, dflags);
> +			dump_sub_desc(btsk_node->btsk);
> +			btsk_node = btsk_node->next;
> +		}
>   
> -	case DSA_OPCODE_COMPARE:
> -		dsa_prep_batch_compare(ctx->batch_task);
> -		break;
> +		btsk_node = ctx->multi_btask_node;
> +		while (btsk_node) {
> +			dsa_desc_submit(ctx, btsk_node->btsk->core_task->desc);
> +			btsk_node = btsk_node->next;
> +		}
>   
> -	case DSA_OPCODE_COMPVAL:
> -		dsa_prep_batch_compval(ctx->batch_task);
> -		break;
> -	case DSA_OPCODE_DUALCAST:
> -		dsa_prep_batch_dualcast(ctx->batch_task);
> -		break;
> -	default:
> -		err("Unsupported op %#x\n", bopcode);
> -		return -EINVAL;
> -	}
> +		btsk_node = ctx->multi_btask_node;
> +		while (btsk_node) {
> +			rc = dsa_wait_batch(btsk_node->btsk);
> +			if (rc != DSA_STATUS_OK)
> +				err("batch failed stat %d\n", rc);
> +			btsk_node = btsk_node->next;
> +		}
>   
> -	dsa_prep_batch(ctx->batch_task, dflags);
> -	dump_sub_desc(ctx->batch_task);
> -	dsa_desc_submit(ctx, ctx->batch_task->core_task->desc);
> +		btsk_node = ctx->multi_btask_node;
> +		while (btsk_node) {
> +			rc = batch_result_verify(btsk_node->btsk, dflags & IDXD_OP_FLAG_BOF);
> +			btsk_node = btsk_node->next;
> +		}
>   
> -	rc = dsa_wait_batch(ctx);
> -	if (rc != DSA_STATUS_OK) {
> -		err("batch failed stat %d\n", rc);
> -		rc = -ENXIO;
> +		dsa_free_task(ctx);
> +		itr = itr - range;
>   	}
>   
> -	rc = batch_result_verify(ctx->batch_task, dflags & IDXD_OP_FLAG_BOF);
> -
>   	return rc;
>   }
>   
> -static int test_noop(struct dsa_context *ctx, int tflags)
> +static int test_noop(struct dsa_context *ctx, int tflags, int num_desc)
>   {
> -	struct task *tsk;
> -	int rc;
> +	struct task_node *tsk_node;
> +	int rc = DSA_STATUS_OK;
> +	int itr = num_desc, i = 0, range = 0;
>   
> -	info("noop: tflags %#x\n", tflags);
> +	info("testnoop: tflags %#x num_desc %ld\n", tflags, num_desc);
>   
>   	ctx->is_batch = 0;
>   
> -	rc = alloc_task(ctx);
> -	if (rc != DSA_STATUS_OK) {
> -		err("noop: alloc task failed, rc=%d\n", rc);
> -		return rc;
> -	}
> +	if (ctx->dedicated == ACCFG_WQ_SHARED)
> +		range = ctx->threshold;
> +	else
> +		range = ctx->wq_size - 1;
>   
> -	tsk = ctx->single_task;
> +	while (itr > 0 && rc == DSA_STATUS_OK) {
> +		i = (itr < range) ? itr : range;
> +		/* Allocate memory to all the task nodes, desc, completion record*/
> +		rc = alloc_multiple_tasks(ctx, i);
> +		if (rc != DSA_STATUS_OK)
> +			return rc;
>   
> -	rc = dsa_noop(ctx);
> -	if (rc != DSA_STATUS_OK) {
> -		err("noop failed stat %d\n", rc);
> -		return rc;
> -	}
> +		/* allocate memory to src and dest buffers and fill in the desc for all the nodes*/
> +		tsk_node = ctx->multi_task_node;
> +		while (tsk_node) {
> +			tsk_node->tsk->opcode = DSA_OPCODE_NOOP;
> +			tsk_node->tsk->test_flags = tflags;
> +			tsk_node = tsk_node->next;
> +		}
> +
> +		rc = dsa_noop_multi_task_nodes(ctx);
> +		if (rc != DSA_STATUS_OK)
> +			return rc;
>   
> -	rc = task_result_verify(tsk, 0);
> -	if (rc != DSA_STATUS_OK)
> -		return rc;
> +		/* Verification of all the nodes*/
> +		tsk_node = ctx->multi_task_node;
> +		while (tsk_node) {
> +			rc = task_result_verify(tsk_node->tsk, 0);
> +			tsk_node = tsk_node->next;
> +		}
> +
> +		dsa_free_task(ctx);
> +		itr = itr - range;
> +	}
>   
>   	return rc;
>   }
>   
>   static int test_memory(struct dsa_context *ctx, size_t buf_size,
> -		       int tflags, uint32_t opcode)
> +		       int tflags, uint32_t opcode, int num_desc)
>   {
> -	struct task *tsk;
> -	int rc;
> +	struct task_node *tsk_node;
> +	int rc = DSA_STATUS_OK;
> +	int itr = num_desc, i = 0, range = 0;
>   
> -	info("mem: len %#lx tflags %#x opcode %d\n", buf_size, tflags, opcode);
> +	info("testmemory: opcode %d len %#lx tflags %#x num_desc %ld\n",
> +	     opcode, buf_size, tflags, num_desc);
>   
>   	ctx->is_batch = 0;
>   
> -	rc = alloc_task(ctx);
> -	if (rc != DSA_STATUS_OK) {
> -		err("mem: alloc task failed opcode %d, rc=%d\n", opcode, rc);
> -		return rc;
> -	}
> -
> -	tsk = ctx->single_task;
> -	rc = init_task(tsk, tflags, opcode, buf_size);
> -	if (rc != DSA_STATUS_OK) {
> -		err("mem: init task failed opcode %d, rc=%d\n", opcode, rc);
> -		return rc;
> -	}
> -
> -	switch (opcode) {
> -	case DSA_OPCODE_MEMMOVE:
> -		rc = dsa_memcpy(ctx);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -
> -		rc = task_result_verify(tsk, 0);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -		break;
> -
> -	case DSA_OPCODE_MEMFILL:
> -		rc = dsa_memfill(ctx);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -
> -		rc = task_result_verify(tsk, 0);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -		break;
> +	if (ctx->dedicated == ACCFG_WQ_SHARED)
> +		range = ctx->threshold;
> +	else
> +		range = ctx->wq_size - 1;
>   
> -	case DSA_OPCODE_COMPARE:
> -		rc = dsa_compare(ctx);
> +	while (itr > 0 && rc == DSA_STATUS_OK) {
> +		i = (itr < range) ? itr : range;
> +		/* Allocate memory to all the task nodes, desc, completion record*/
> +		rc = alloc_multiple_tasks(ctx, i);
>   		if (rc != DSA_STATUS_OK)
>   			return rc;
>   
> -		rc = task_result_verify(tsk, 0);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> +		/* allocate memory to src and dest buffers and fill in the desc for all the nodes*/
> +		tsk_node = ctx->multi_task_node;
> +		while (tsk_node) {
> +			tsk_node->tsk->xfer_size = buf_size;
>   
> -		info("Testing mismatch buffers\n");
> -		info("creating a diff at index %#lx\n", tsk->xfer_size / 2);
> -		((uint8_t *)(tsk->src1))[tsk->xfer_size / 2] = 0;
> -		((uint8_t *)(tsk->src2))[tsk->xfer_size / 2] = 1;
> +			rc = init_task(tsk_node->tsk, tflags, opcode, buf_size);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
>   
> -		memset(tsk->comp, 0, sizeof(struct dsa_completion_record));
> +			tsk_node = tsk_node->next;
> +		}
>   
> -		rc = dsa_compare(ctx);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> +		switch (opcode) {
> +		case DSA_OPCODE_MEMMOVE:
> +			rc = dsa_memcpy_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
>   
> -		rc = task_result_verify(tsk, 1);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -		break;
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 0);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +			break;
>   
> -	case DSA_OPCODE_COMPVAL:
> -		rc = dsa_compval(ctx);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> +		case DSA_OPCODE_MEMFILL:
> +			rc = dsa_memfill_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
>   
> -		rc = task_result_verify(tsk, 0);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 0);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +			break;
>   
> -		info("Testing mismatching buffers\n");
> -		info("creating a diff at index %#lx\n", tsk->xfer_size / 2);
> -		((uint8_t *)(tsk->src1))[tsk->xfer_size / 2] =
> -				~(((uint8_t *)(tsk->src1))[tsk->xfer_size / 2]);
> +		case DSA_OPCODE_COMPARE:
> +			rc = dsa_compare_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 0);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +
> +			info("Testing mismatch buffers\n");
> +			tsk_node = ctx->multi_task_node;
> +			while (tsk_node) {
> +				((uint8_t *)(tsk_node->tsk->src1))[tsk_node->tsk->xfer_size / 2] =
> +					0;
> +				((uint8_t *)(tsk_node->tsk->src2))[tsk_node->tsk->xfer_size / 2] =
> +					1;
> +				memset(tsk_node->tsk->comp, 0,
> +				       sizeof(struct dsa_completion_record));
> +				tsk_node = tsk_node->next;
> +			}
>   
> -		memset(tsk->comp, 0, sizeof(struct dsa_completion_record));
> +			rc = dsa_compare_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
>   
> -		rc = dsa_compval(ctx);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 1);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +			break;
>   
> -		rc = task_result_verify(tsk, 1);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -		break;
> +		case DSA_OPCODE_COMPVAL:
> +			rc = dsa_compval_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 0);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +
> +			info("Testing mismatching buffers\n");
> +			tsk_node = ctx->multi_task_node;
> +			while (tsk_node) {
> +				((uint8_t *)(tsk_node->tsk->src1))[tsk_node->tsk->xfer_size / 2] =
> +				~(((uint8_t *)(tsk_node->tsk->src1))[tsk_node->tsk->xfer_size / 2]);
> +				memset(tsk_node->tsk->comp, 0,
> +				       sizeof(struct dsa_completion_record));
> +				tsk_node = tsk_node->next;
> +			}
>   
> -	case DSA_OPCODE_DUALCAST:
> -		rc = dsa_dualcast(ctx);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> +			rc = dsa_compval_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
>   
> -		rc = task_result_verify(tsk, 0);
> -		if (rc != DSA_STATUS_OK)
> -			return rc;
> -		break;
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 1);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +			break;
> +		case DSA_OPCODE_DUALCAST:
> +			rc = dsa_dualcast_multi_task_nodes(ctx);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +
> +			/* Verification of all the nodes*/
> +			rc = task_result_verify_task_nodes(ctx, 0);
> +			if (rc != DSA_STATUS_OK)
> +				return rc;
> +			break;
> +		default:
> +			err("Unsupported op %#x\n", opcode);
> +			return -EINVAL;
> +		}
>   
> -	default:
> -		err("Unsupported opcode %#x\n", opcode);
> -		return -EINVAL;
> +		dsa_free_task(ctx);
> +		itr = itr - range;
>   	}
>   
>   	return rc;
> @@ -257,8 +341,9 @@ int main(int argc, char *argv[])
>   	int wq_id = DSA_DEVICE_ID_NO_INPUT;
>   	int dev_id = DSA_DEVICE_ID_NO_INPUT;
>   	int dev_wq_id = DSA_DEVICE_ID_NO_INPUT;
> +	unsigned int num_desc = 1;
>   
> -	while ((opt = getopt(argc, argv, "w:l:f:o:b:c:d:t:p:vh")) != -1) {
> +	while ((opt = getopt(argc, argv, "w:l:f:o:b:c:d:n:t:p:vh")) != -1) {
>   		switch (opt) {
>   		case 'w':
>   			wq_type = atoi(optarg);
> @@ -286,6 +371,9 @@ int main(int argc, char *argv[])
>   				return -EINVAL;
>   			}
>   			break;
> +		case 'n':
> +			num_desc = strtoul(optarg, NULL, 0);
> +			break;
>   		case 't':
>   			ms_timeout = strtoul(optarg, NULL, 0);
>   			break;
> @@ -316,7 +404,7 @@ int main(int argc, char *argv[])
>   
>   	switch (opcode) {
>   	case DSA_OPCODE_NOOP:
> -		rc = test_noop(dsa, tflags);
> +		rc = test_noop(dsa, tflags, num_desc);
>   		if (rc != DSA_STATUS_OK)
>   			goto error;
>   		break;
> @@ -327,7 +415,7 @@ int main(int argc, char *argv[])
>   			rc = -EINVAL;
>   			goto error;
>   		}
> -		rc = test_batch(dsa, buf_size, tflags, bopcode, bsize);
> +		rc = test_batch(dsa, buf_size, tflags, bopcode, bsize, num_desc);
>   		if (rc < 0)
>   			goto error;
>   		break;
> @@ -337,7 +425,7 @@ int main(int argc, char *argv[])
>   	case DSA_OPCODE_COMPARE:
>   	case DSA_OPCODE_COMPVAL:
>   	case DSA_OPCODE_DUALCAST:
> -		rc = test_memory(dsa, buf_size, tflags, opcode);
> +		rc = test_memory(dsa, buf_size, tflags, opcode, num_desc);
>   		if (rc != DSA_STATUS_OK)
>   			goto error;
>   		break;
> diff --git a/test/prep.c b/test/prep.c
> index ab18e53..80e977c 100644
> --- a/test/prep.c
> +++ b/test/prep.c
> @@ -51,10 +51,10 @@ void dsa_prep_memcpy(struct task *tsk)
>   	tsk->comp->status = 0;
>   }
>   
> -void dsa_reprep_memcpy(struct dsa_context *ctx)
> +void dsa_reprep_memcpy(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_completion_record *compl = ctx->single_task->comp;
> -	struct dsa_hw_desc *hw = ctx->single_task->desc;
> +	struct dsa_completion_record *compl = tsk->comp;
> +	struct dsa_hw_desc *hw = tsk->desc;
>   
>   	info("PF addr %#lx dir %d bc %#x\n",
>   	     compl->fault_addr, compl->result,
> @@ -119,10 +119,10 @@ void dsa_prep_memfill(struct task *tsk)
>   	tsk->comp->status = 0;
>   }
>   
> -void dsa_reprep_memfill(struct dsa_context *ctx)
> +void dsa_reprep_memfill(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_completion_record *compl = ctx->single_task->comp;
> -	struct dsa_hw_desc *hw = ctx->single_task->desc;
> +	struct dsa_completion_record *compl = tsk->comp;
> +	struct dsa_hw_desc *hw = tsk->desc;
>   
>   	info("PF addr %#lx dir %d bc %#x\n",
>   	     compl->fault_addr, compl->result,
> @@ -166,10 +166,10 @@ void dsa_prep_compare(struct task *tsk)
>   	tsk->comp->status = 0;
>   }
>   
> -void dsa_reprep_compare(struct dsa_context *ctx)
> +void dsa_reprep_compare(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_completion_record *compl = ctx->single_task->comp;
> -	struct dsa_hw_desc *hw = ctx->single_task->desc;
> +	struct dsa_completion_record *compl = tsk->comp;
> +	struct dsa_hw_desc *hw = tsk->desc;
>   
>   	info("PF addr %#lx dir %d bc %#x\n",
>   	     compl->fault_addr, compl->result,
> @@ -214,10 +214,10 @@ void dsa_prep_compval(struct task *tsk)
>   	tsk->comp->status = 0;
>   }
>   
> -void dsa_reprep_compval(struct dsa_context *ctx)
> +void dsa_reprep_compval(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_completion_record *compl = ctx->single_task->comp;
> -	struct dsa_hw_desc *hw = ctx->single_task->desc;
> +	struct dsa_completion_record *compl = tsk->comp;
> +	struct dsa_hw_desc *hw = tsk->desc;
>   
>   	info("PF addr %#lx dir %d bc %#x\n",
>   	     compl->fault_addr, compl->result,
> @@ -262,10 +262,10 @@ void dsa_prep_dualcast(struct task *tsk)
>   	tsk->comp->status = 0;
>   }
>   
> -void dsa_reprep_dualcast(struct dsa_context *ctx)
> +void dsa_reprep_dualcast(struct dsa_context *ctx, struct task *tsk)
>   {
> -	struct dsa_completion_record *compl = ctx->single_task->comp;
> -	struct dsa_hw_desc *hw = ctx->single_task->desc;
> +	struct dsa_completion_record *compl = tsk->comp;
> +	struct dsa_hw_desc *hw = tsk->desc;
>   
>   	info("PF addr %#lx dir %d bc %#x\n",
>   	     compl->fault_addr, compl->result,
>