vhost_crypto_process_one_req(struct vhost_crypto *vcrypto,
		struct vhost_virtqueue *vq, struct rte_crypto_op *op,
		struct vring_desc *head, uint16_t desc_idx)
{
	struct vhost_crypto_data_req *vc_req = rte_mbuf_to_priv(op->sym->m_src);
	struct rte_cryptodev_sym_session *session;
	struct virtio_crypto_op_data_req *req, tmp_req;
	struct virtio_crypto_inhdr *inhdr;
	struct vring_desc *desc = NULL;
	uint64_t session_id;
	uint64_t dlen;
	uint32_t nb_descs = vq->size;
	int err = 0;

	vc_req->desc_idx = desc_idx;
	vc_req->dev = vcrypto->dev;
	vc_req->vq = vq;

	if (likely(head->flags & VRING_DESC_F_INDIRECT)) {
		dlen = head->len;
		nb_descs = dlen / sizeof(struct vring_desc);
		/* drop invalid descriptors */
		if (unlikely(nb_descs > vq->size))
			return -1;
		desc = IOVA_TO_VVA(struct vring_desc *, vc_req, head->addr,
				&dlen, VHOST_ACCESS_RO);
		if (unlikely(!desc || dlen != head->len))
			return -1;
		desc_idx = 0;
		head = desc;
	} else {
		desc = head;
	}

	vc_req->head = head;
	vc_req->zero_copy = vcrypto->option;

	req = get_data_ptr(vc_req, desc, VHOST_ACCESS_RO);
	if (unlikely(req == NULL)) {
		switch (vcrypto->option) {
		case RTE_VHOST_CRYPTO_ZERO_COPY_ENABLE:
			err = VIRTIO_CRYPTO_BADMSG;
			VC_LOG_ERR("Invalid descriptor");
			goto error_exit;
		case RTE_VHOST_CRYPTO_ZERO_COPY_DISABLE:
			req = &tmp_req;
			if (unlikely(copy_data(req, vc_req, &desc, sizeof(*req),
					&nb_descs, vq->size) < 0)) {
				err = VIRTIO_CRYPTO_BADMSG;
				VC_LOG_ERR("Invalid descriptor");
				goto error_exit;
			}
			break;
		default:
			err = VIRTIO_CRYPTO_ERR;
			VC_LOG_ERR("Invalid option");
			goto error_exit;
		}
	} else {
		if (unlikely(move_desc(vc_req->head, &desc,
				sizeof(*req), &nb_descs, vq->size) < 0)) {
			VC_LOG_ERR("Incorrect descriptor");
			goto error_exit;
		}
	}

	switch (req->header.opcode) {
	case VIRTIO_CRYPTO_CIPHER_ENCRYPT:
	case VIRTIO_CRYPTO_CIPHER_DECRYPT:
		session_id = req->header.session_id;

		/* one branch to avoid unnecessary table lookup */
		if (vcrypto->cache_session_id != session_id) {
			err = rte_hash_lookup_data(vcrypto->session_map,
					&session_id, (void **)&session);
			if (unlikely(err < 0)) {
				err = VIRTIO_CRYPTO_ERR;
				VC_LOG_ERR("Failed to find session %"PRIu64,
						session_id);
				goto error_exit;
			}

			vcrypto->cache_session = session;
			vcrypto->cache_session_id = session_id;
		}

		session = vcrypto->cache_session;

		err = rte_crypto_op_attach_sym_session(op, session);
		if (unlikely(err < 0)) {
			err = VIRTIO_CRYPTO_ERR;
			VC_LOG_ERR("Failed to attach session to op");
			goto error_exit;
		}

		switch (req->u.sym_req.op_type) {
		case VIRTIO_CRYPTO_SYM_OP_NONE:
			err = VIRTIO_CRYPTO_NOTSUPP;
			break;
		case VIRTIO_CRYPTO_SYM_OP_CIPHER:
			err = prepare_sym_cipher_op(vcrypto, op, vc_req,
					&req->u.sym_req.u.cipher, desc,
					&nb_descs, vq->size);
			break;
		case VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING:
			err = prepare_sym_chain_op(vcrypto, op, vc_req,
					&req->u.sym_req.u.chain, desc,
					&nb_descs, vq->size);
			break;
		}
		if (unlikely(err != 0)) {
			VC_LOG_ERR("Failed to process sym request");
			goto error_exit;
		}
		break;
	default:
		VC_LOG_ERR("Unsupported symmetric crypto request type %u",
				req->header.opcode);
		goto error_exit;
	}

	return 0;

error_exit:

	inhdr = reach_inhdr(vc_req, desc, &nb_descs, vq->size);
	if (likely(inhdr != NULL))
		inhdr->status = (uint8_t)err;

	return -1;
}