qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] Add a new backend for cryptodev
@ 2022-10-08  8:50 Lei He
  2022-10-08  8:50 ` [PATCH v2 1/4] virtio-crypto: Support asynchronous mode Lei He
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Lei He @ 2022-10-08  8:50 UTC (permalink / raw)
  To: berrange, arei.gonglei, mst; +Cc: pizhenwei, qemu-devel, Lei He

v1 --> v2:
- Fix compile errors when neither 'nettle' nor 'gcrypt' are enabled.
- Trivial changes to error codes when neither 'nettle' nor 'gcrypt' are
enabled.

This patch adds a new backend called LKCF to cryptodev, LKCF stands
for Linux Kernel Cryptography Framework. If a cryptographic
accelerator that supports LKCF is installed on the the host (you can
see which algorithms are supported in host's LKCF by executing
'cat /proc/crypto'), then RSA operations can be offloaded.
More background info can refer to: https://lwn.net/Articles/895399/,
'keyctl[5]' in the picture.

This patch:
1. Modified some interfaces of cryptodev and cryptodev-backend to
support asynchronous requests.
2. Extended the DER encoder in crypto, so that we can export the
RSA private key into PKCS#8 format and upload it to host kernel.
3. Added a new backend for cryptodev.

I tested the backend with a QAT card, the qps of RSA-2048-decryption
is about 25k/s, and the main-loop becomes the bottleneck. The qps
using OpenSSL directly is about 6k/s (with 6 vCPUs). We will support 
IO-thread for cryptodev in another series later.


Lei He (4):
  virtio-crypto: Support asynchronous mode
  crypto: Support DER encodings
  crypto: Support export akcipher to pkcs8
  cryptodev: Add a lkcf-backend for cryptodev

 backends/cryptodev-builtin.c    |  69 +++--
 backends/cryptodev-lkcf.c       | 645 ++++++++++++++++++++++++++++++++++++++++
 backends/cryptodev-vhost-user.c |  51 +++-
 backends/cryptodev.c            |  44 +--
 backends/meson.build            |   3 +
 crypto/akcipher.c               |  18 ++
 crypto/der.c                    | 307 +++++++++++++++++--
 crypto/der.h                    | 211 ++++++++++++-
 crypto/rsakey.c                 |  42 +++
 crypto/rsakey.h                 |  11 +-
 hw/virtio/virtio-crypto.c       | 324 +++++++++++---------
 include/crypto/akcipher.h       |  21 ++
 include/sysemu/cryptodev.h      |  61 ++--
 qapi/qom.json                   |   2 +
 tests/unit/test-crypto-der.c    | 126 ++++++--
 15 files changed, 1675 insertions(+), 260 deletions(-)
 create mode 100644 backends/cryptodev-lkcf.c

-- 
2.11.0



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2 1/4] virtio-crypto: Support asynchronous mode
  2022-10-08  8:50 [PATCH v2 0/4] Add a new backend for cryptodev Lei He
@ 2022-10-08  8:50 ` Lei He
  2022-11-01 10:37   ` Michael S. Tsirkin
  2022-10-08  8:50 ` [PATCH v2 2/4] crypto: Support DER encodings Lei He
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Lei He @ 2022-10-08  8:50 UTC (permalink / raw)
  To: berrange, arei.gonglei, mst; +Cc: pizhenwei, qemu-devel, Lei He

virtio-crypto: Modify the current interface of virtio-crypto
device to support asynchronous mode.

Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 backends/cryptodev-builtin.c    |  69 ++++++---
 backends/cryptodev-vhost-user.c |  51 +++++--
 backends/cryptodev.c            |  44 +++---
 hw/virtio/virtio-crypto.c       | 324 ++++++++++++++++++++++------------------
 include/sysemu/cryptodev.h      |  60 +++++---
 5 files changed, 336 insertions(+), 212 deletions(-)

diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 125cbad1d3..cda6ca3b71 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -355,42 +355,62 @@ static int cryptodev_builtin_create_akcipher_session(
     return index;
 }
 
-static int64_t cryptodev_builtin_create_session(
+static int cryptodev_builtin_create_session(
            CryptoDevBackend *backend,
            CryptoDevBackendSessionInfo *sess_info,
-           uint32_t queue_index, Error **errp)
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque)
 {
     CryptoDevBackendBuiltin *builtin =
                       CRYPTODEV_BACKEND_BUILTIN(backend);
     CryptoDevBackendSymSessionInfo *sym_sess_info;
     CryptoDevBackendAsymSessionInfo *asym_sess_info;
+    int ret, status;
+    Error *local_error = NULL;
 
     switch (sess_info->op_code) {
     case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
         sym_sess_info = &sess_info->u.sym_sess_info;
-        return cryptodev_builtin_create_cipher_session(
-                           builtin, sym_sess_info, errp);
+        ret = cryptodev_builtin_create_cipher_session(
+                    builtin, sym_sess_info, &local_error);
+        break;
 
     case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
         asym_sess_info = &sess_info->u.asym_sess_info;
-        return cryptodev_builtin_create_akcipher_session(
-                           builtin, asym_sess_info, errp);
+        ret = cryptodev_builtin_create_akcipher_session(
+                           builtin, asym_sess_info, &local_error);
+        break;
 
     case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
     case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
     default:
-        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
+        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
                    sess_info->op_code);
-        return -1;
+        return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
-    return -1;
+    if (local_error) {
+        error_report_err(local_error);
+    }
+    if (ret < 0) {
+        status = -VIRTIO_CRYPTO_ERR;
+    } else {
+        sess_info->session_id = ret;
+        status = VIRTIO_CRYPTO_OK;
+    }
+    if (cb) {
+        cb(opaque, status);
+    }
+    return 0;
 }
 
 static int cryptodev_builtin_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
-           uint32_t queue_index, Error **errp)
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque)
 {
     CryptoDevBackendBuiltin *builtin =
                       CRYPTODEV_BACKEND_BUILTIN(backend);
@@ -407,6 +427,9 @@ static int cryptodev_builtin_close_session(
 
     g_free(session);
     builtin->sessions[session_id] = NULL;
+    if (cb) {
+        cb(opaque, VIRTIO_CRYPTO_OK);
+    }
     return 0;
 }
 
@@ -506,7 +529,9 @@ static int cryptodev_builtin_asym_operation(
 static int cryptodev_builtin_operation(
                  CryptoDevBackend *backend,
                  CryptoDevBackendOpInfo *op_info,
-                 uint32_t queue_index, Error **errp)
+                 uint32_t queue_index,
+                 CryptoDevCompletionFunc cb,
+                 void *opaque)
 {
     CryptoDevBackendBuiltin *builtin =
                       CRYPTODEV_BACKEND_BUILTIN(backend);
@@ -514,11 +539,12 @@ static int cryptodev_builtin_operation(
     CryptoDevBackendSymOpInfo *sym_op_info;
     CryptoDevBackendAsymOpInfo *asym_op_info;
     enum CryptoDevBackendAlgType algtype = op_info->algtype;
-    int ret = -VIRTIO_CRYPTO_ERR;
+    int status = -VIRTIO_CRYPTO_ERR;
+    Error *local_error = NULL;
 
     if (op_info->session_id >= MAX_NUM_SESSIONS ||
               builtin->sessions[op_info->session_id] == NULL) {
-        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
+        error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
                    op_info->session_id);
         return -VIRTIO_CRYPTO_INVSESS;
     }
@@ -526,14 +552,21 @@ static int cryptodev_builtin_operation(
     sess = builtin->sessions[op_info->session_id];
     if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
         sym_op_info = op_info->u.sym_op_info;
-        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
+        status = cryptodev_builtin_sym_operation(sess, sym_op_info,
+                                                 &local_error);
     } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
         asym_op_info = op_info->u.asym_op_info;
-        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
-                                               asym_op_info, errp);
+        status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
+                                                  asym_op_info, &local_error);
     }
 
-    return ret;
+    if (local_error) {
+        error_report_err(local_error);
+    }
+    if (cb) {
+        cb(opaque, status);
+    }
+    return 0;
 }
 
 static void cryptodev_builtin_cleanup(
@@ -548,7 +581,7 @@ static void cryptodev_builtin_cleanup(
 
     for (i = 0; i < MAX_NUM_SESSIONS; i++) {
         if (builtin->sessions[i] != NULL) {
-            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
+            cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
         }
     }
 
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index 5443a59153..351b2729e0 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -259,13 +259,18 @@ static int64_t cryptodev_vhost_user_sym_create_session(
     return -1;
 }
 
-static int64_t cryptodev_vhost_user_create_session(
+static int cryptodev_vhost_user_create_session(
            CryptoDevBackend *backend,
            CryptoDevBackendSessionInfo *sess_info,
-           uint32_t queue_index, Error **errp)
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque)
 {
     uint32_t op_code = sess_info->op_code;
     CryptoDevBackendSymSessionInfo *sym_sess_info;
+    int64_t ret;
+    Error *local_error = NULL;
+    int status;
 
     switch (op_code) {
     case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
@@ -273,27 +278,42 @@ static int64_t cryptodev_vhost_user_create_session(
     case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
     case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
         sym_sess_info = &sess_info->u.sym_sess_info;
-        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
-                   queue_index, errp);
+        ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
+                   queue_index, &local_error);
+        break;
+
     default:
-        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
+        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
                    sess_info->op_code);
-        return -1;
-
+        return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
-    return -1;
+    if (local_error) {
+        error_report_err(local_error);
+    }
+    if (ret < 0) {
+        status = -VIRTIO_CRYPTO_ERR;
+    } else {
+        sess_info->session_id = ret;
+        status = VIRTIO_CRYPTO_OK;
+    }
+    if (cb) {
+        cb(opaque, status);
+    }
+    return 0;
 }
 
 static int cryptodev_vhost_user_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
-           uint32_t queue_index, Error **errp)
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque)
 {
     CryptoDevBackendClient *cc =
                   backend->conf.peers.ccs[queue_index];
     CryptoDevBackendVhost *vhost_crypto;
-    int ret;
+    int ret = -1, status;
 
     vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
     if (vhost_crypto) {
@@ -301,12 +321,17 @@ static int cryptodev_vhost_user_close_session(
         ret = dev->vhost_ops->vhost_crypto_close_session(dev,
                                                          session_id);
         if (ret < 0) {
-            return -1;
+            status = -VIRTIO_CRYPTO_ERR;
         } else {
-            return 0;
+            status = VIRTIO_CRYPTO_OK;
         }
+    } else {
+        status = -VIRTIO_CRYPTO_NOTSUPP;
     }
-    return -1;
+    if (cb) {
+        cb(opaque, status);
+    }
+    return 0;
 }
 
 static void cryptodev_vhost_user_cleanup(
diff --git a/backends/cryptodev.c b/backends/cryptodev.c
index 33eb4e1a70..54ee8c81f5 100644
--- a/backends/cryptodev.c
+++ b/backends/cryptodev.c
@@ -26,6 +26,7 @@
 #include "qapi/error.h"
 #include "qapi/visitor.h"
 #include "qemu/config-file.h"
+#include "qemu/error-report.h"
 #include "qom/object_interfaces.h"
 #include "hw/virtio/virtio-crypto.h"
 
@@ -72,69 +73,72 @@ void cryptodev_backend_cleanup(
     }
 }
 
-int64_t cryptodev_backend_create_session(
+int cryptodev_backend_create_session(
            CryptoDevBackend *backend,
            CryptoDevBackendSessionInfo *sess_info,
-           uint32_t queue_index, Error **errp)
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque)
 {
     CryptoDevBackendClass *bc =
                       CRYPTODEV_BACKEND_GET_CLASS(backend);
 
     if (bc->create_session) {
-        return bc->create_session(backend, sess_info, queue_index, errp);
+        return bc->create_session(backend, sess_info, queue_index, cb, opaque);
     }
-
-    return -1;
+    return -VIRTIO_CRYPTO_NOTSUPP;
 }
 
 int cryptodev_backend_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
-           uint32_t queue_index, Error **errp)
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque)
 {
     CryptoDevBackendClass *bc =
                       CRYPTODEV_BACKEND_GET_CLASS(backend);
 
     if (bc->close_session) {
-        return bc->close_session(backend, session_id, queue_index, errp);
+        return bc->close_session(backend, session_id, queue_index, cb, opaque);
     }
-
-    return -1;
+    return -VIRTIO_CRYPTO_NOTSUPP;
 }
 
 static int cryptodev_backend_operation(
                  CryptoDevBackend *backend,
                  CryptoDevBackendOpInfo *op_info,
-                 uint32_t queue_index, Error **errp)
+                 uint32_t queue_index,
+                 CryptoDevCompletionFunc cb,
+                 void *opaque)
 {
     CryptoDevBackendClass *bc =
                       CRYPTODEV_BACKEND_GET_CLASS(backend);
 
     if (bc->do_op) {
-        return bc->do_op(backend, op_info, queue_index, errp);
+        return bc->do_op(backend, op_info, queue_index, cb, opaque);
     }
-
-    return -VIRTIO_CRYPTO_ERR;
+    return -VIRTIO_CRYPTO_NOTSUPP;
 }
 
 int cryptodev_backend_crypto_operation(
                  CryptoDevBackend *backend,
-                 void *opaque,
-                 uint32_t queue_index, Error **errp)
+                 void *opaque1,
+                 uint32_t queue_index,
+                 CryptoDevCompletionFunc cb, void *opaque2)
 {
-    VirtIOCryptoReq *req = opaque;
+    VirtIOCryptoReq *req = opaque1;
     CryptoDevBackendOpInfo *op_info = &req->op_info;
     enum CryptoDevBackendAlgType algtype = req->flags;
 
     if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
         && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
-        error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
-                   algtype);
-
+        error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype);
         return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
-    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
+    return cryptodev_backend_operation(backend, op_info, queue_index,
+                                       cb, opaque2);
 }
 
 static void
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index df4bde210b..39c8f5914e 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -27,6 +27,39 @@
 
 #define VIRTIO_CRYPTO_VM_VERSION 1
 
+typedef struct VirtIOCryptoSessionReq {
+    VirtIODevice *vdev;
+    VirtQueue *vq;
+    VirtQueueElement *elem;
+    CryptoDevBackendSessionInfo info;
+    CryptoDevCompletionFunc cb;
+} VirtIOCryptoSessionReq;
+
+static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq)
+{
+    switch (sreq->info.op_code) {
+    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
+        g_free(sreq->info.u.sym_sess_info.cipher_key);
+        g_free(sreq->info.u.sym_sess_info.auth_key);
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+        g_free(sreq->info.u.asym_sess_info.key);
+        break;
+
+    case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
+    case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
+    case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
+    case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
+    case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
+        break;
+
+    default:
+        error_report("Unknown opcode: %u", sreq->info.op_code);
+    }
+    g_free(sreq);
+}
+
 /*
  * Transfer virtqueue index to crypto queue index.
  * The control virtqueue is after the data virtqueues
@@ -75,27 +108,24 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
     return 0;
 }
 
-static int64_t
+static int
 virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
                struct virtio_crypto_sym_create_session_req *sess_req,
                uint32_t queue_id,
                uint32_t opcode,
-               struct iovec *iov, unsigned int out_num)
+               struct iovec *iov, unsigned int out_num,
+               VirtIOCryptoSessionReq *sreq)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
-    CryptoDevBackendSessionInfo info;
-    CryptoDevBackendSymSessionInfo *sym_info;
-    int64_t session_id;
+    CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info;
     int queue_index;
     uint32_t op_type;
-    Error *local_err = NULL;
     int ret;
 
-    memset(&info, 0, sizeof(info));
     op_type = ldl_le_p(&sess_req->op_type);
-    info.op_code = opcode;
+    sreq->info.op_code = opcode;
 
-    sym_info = &info.u.sym_sess_info;
+    sym_info = &sreq->info.u.sym_sess_info;
     sym_info->op_type = op_type;
 
     if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
@@ -103,7 +133,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
                            &sess_req->u.cipher.para,
                            &iov, &out_num);
         if (ret < 0) {
-            goto err;
+            return ret;
         }
     } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
         size_t s;
@@ -112,7 +142,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
                            &sess_req->u.chain.para.cipher_param,
                            &iov, &out_num);
         if (ret < 0) {
-            goto err;
+            return ret;
         }
         /* hash part */
         sym_info->alg_chain_order = ldl_le_p(
@@ -129,8 +159,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
             if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
                 error_report("virtio-crypto length of auth key is too big: %u",
                              sym_info->auth_key_len);
-                ret = -VIRTIO_CRYPTO_ERR;
-                goto err;
+                return -VIRTIO_CRYPTO_ERR;
             }
             /* get auth key */
             if (sym_info->auth_key_len > 0) {
@@ -140,8 +169,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
                 if (unlikely(s != sym_info->auth_key_len)) {
                     virtio_error(vdev,
                           "virtio-crypto authenticated key incorrect");
-                    ret = -EFAULT;
-                    goto err;
+                    return -EFAULT;
                 }
                 iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
             }
@@ -153,49 +181,30 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
         } else {
             /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
             error_report("unsupported hash mode");
-            ret = -VIRTIO_CRYPTO_NOTSUPP;
-            goto err;
+            return -VIRTIO_CRYPTO_NOTSUPP;
         }
     } else {
         /* VIRTIO_CRYPTO_SYM_OP_NONE */
         error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE");
-        ret = -VIRTIO_CRYPTO_NOTSUPP;
-        goto err;
+        return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
     queue_index = virtio_crypto_vq2q(queue_id);
-    session_id = cryptodev_backend_create_session(
-                                     vcrypto->cryptodev,
-                                     &info, queue_index, &local_err);
-    if (session_id >= 0) {
-        ret = session_id;
-    } else {
-        if (local_err) {
-            error_report_err(local_err);
-        }
-        ret = -VIRTIO_CRYPTO_ERR;
-    }
-
-err:
-    g_free(sym_info->cipher_key);
-    g_free(sym_info->auth_key);
-    return ret;
+    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
+                                            queue_index, sreq->cb, sreq);
 }
 
-static int64_t
+static int
 virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
                struct virtio_crypto_akcipher_create_session_req *sess_req,
                uint32_t queue_id, uint32_t opcode,
-               struct iovec *iov, unsigned int out_num)
+               struct iovec *iov, unsigned int out_num,
+               VirtIOCryptoSessionReq *sreq)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
-    CryptoDevBackendSessionInfo info = {0};
-    CryptoDevBackendAsymSessionInfo *asym_info;
-    int64_t session_id;
+    CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info;
     int queue_index;
     uint32_t algo, keytype, keylen;
-    g_autofree uint8_t *key = NULL;
-    Error *local_err = NULL;
 
     algo = ldl_le_p(&sess_req->para.algo);
     keytype = ldl_le_p(&sess_req->para.keytype);
@@ -208,20 +217,19 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
     }
 
     if (keylen) {
-        key = g_malloc(keylen);
-        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
+        asym_info->key = g_malloc(keylen);
+        if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
             virtio_error(vdev, "virtio-crypto asym key incorrect");
             return -EFAULT;
         }
         iov_discard_front(&iov, &out_num, keylen);
     }
 
-    info.op_code = opcode;
-    asym_info = &info.u.asym_sess_info;
+    sreq->info.op_code = opcode;
+    asym_info = &sreq->info.u.asym_sess_info;
     asym_info->algo = algo;
     asym_info->keytype = keytype;
     asym_info->keylen = keylen;
-    asym_info->key = key;
     switch (asym_info->algo) {
     case VIRTIO_CRYPTO_AKCIPHER_RSA:
         asym_info->u.rsa.padding_algo =
@@ -237,45 +245,95 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
     }
 
     queue_index = virtio_crypto_vq2q(queue_id);
-    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
-                     queue_index, &local_err);
-    if (session_id < 0) {
-        if (local_err) {
-            error_report_err(local_err);
-        }
-        return -VIRTIO_CRYPTO_ERR;
-    }
-
-    return session_id;
+    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
+                                            queue_index, sreq->cb, sreq);
 }
 
-static uint8_t
+static int
 virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
          struct virtio_crypto_destroy_session_req *close_sess_req,
-         uint32_t queue_id)
+         uint32_t queue_id,
+         VirtIOCryptoSessionReq *sreq)
 {
-    int ret;
     uint64_t session_id;
-    uint32_t status;
-    Error *local_err = NULL;
 
     session_id = ldq_le_p(&close_sess_req->session_id);
     DPRINTF("close session, id=%" PRIu64 "\n", session_id);
 
-    ret = cryptodev_backend_close_session(
-              vcrypto->cryptodev, session_id, queue_id, &local_err);
-    if (ret == 0) {
-        status = VIRTIO_CRYPTO_OK;
+    return cryptodev_backend_close_session(
+                vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq);
+}
+
+static void virtio_crypto_create_session_completion(void *opaque, int ret)
+{
+    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
+    VirtQueue *vq = sreq->vq;
+    VirtQueueElement *elem = sreq->elem;
+    VirtIODevice *vdev = sreq->vdev;
+    struct virtio_crypto_session_input input;
+    struct iovec *in_iov = elem->in_sg;
+    unsigned in_num = elem->in_num;
+    size_t s;
+
+    memset(&input, 0, sizeof(input));
+    /* Serious errors, need to reset virtio crypto device */
+    if (ret == -EFAULT) {
+        virtqueue_detach_element(vq, elem, 0);
+        goto out;
+    } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
+        stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
+    } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) {
+        stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED);
+    } else if (ret != VIRTIO_CRYPTO_OK) {
+        stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
     } else {
-        if (local_err) {
-            error_report_err(local_err);
-        } else {
-            error_report("destroy session failed");
-        }
+        /* Set the session id */
+        stq_le_p(&input.session_id, sreq->info.session_id);
+        stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
+    }
+
+    s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
+    if (unlikely(s != sizeof(input))) {
+        virtio_error(vdev, "virtio-crypto input incorrect");
+        virtqueue_detach_element(vq, elem, 0);
+        goto out;
+    }
+    virtqueue_push(vq, elem, sizeof(input));
+    virtio_notify(vdev, vq);
+
+out:
+    g_free(elem);
+    virtio_crypto_free_create_session_req(sreq);
+}
+
+static void virtio_crypto_destroy_session_completion(void *opaque, int ret)
+{
+    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
+    VirtQueue *vq = sreq->vq;
+    VirtQueueElement *elem = sreq->elem;
+    VirtIODevice *vdev = sreq->vdev;
+    struct iovec *in_iov = elem->in_sg;
+    unsigned in_num = elem->in_num;
+    uint8_t status;
+    size_t s;
+
+    if (ret < 0) {
         status = VIRTIO_CRYPTO_ERR;
+    } else {
+        status = VIRTIO_CRYPTO_OK;
+    }
+    s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
+    if (unlikely(s != sizeof(status))) {
+        virtio_error(vdev, "virtio-crypto status incorrect");
+        virtqueue_detach_element(vq, elem, 0);
+        goto out;
     }
+    virtqueue_push(vq, elem, sizeof(status));
+    virtio_notify(vdev, vq);
 
-    return status;
+out:
+    g_free(elem);
+    g_free(sreq);
 }
 
 static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
@@ -283,16 +341,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
     VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
     struct virtio_crypto_op_ctrl_req ctrl;
     VirtQueueElement *elem;
-    struct iovec *in_iov;
-    struct iovec *out_iov;
-    unsigned in_num;
+    VirtIOCryptoSessionReq *sreq;
     unsigned out_num;
+    unsigned in_num;
     uint32_t queue_id;
     uint32_t opcode;
     struct virtio_crypto_session_input input;
-    int64_t session_id;
-    uint8_t status;
     size_t s;
+    int ret;
+    struct iovec *out_iov;
+    struct iovec *in_iov;
 
     for (;;) {
         g_autofree struct iovec *out_iov_copy = NULL;
@@ -327,44 +385,34 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
         opcode = ldl_le_p(&ctrl.header.opcode);
         queue_id = ldl_le_p(&ctrl.header.queue_id);
 
-        memset(&input, 0, sizeof(input));
+        sreq = g_new0(VirtIOCryptoSessionReq, 1);
+        sreq->vdev = vdev;
+        sreq->vq = vq;
+        sreq->elem = elem;
+
         switch (opcode) {
         case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
-            session_id = virtio_crypto_create_sym_session(vcrypto,
-                             &ctrl.u.sym_create_session,
-                             queue_id, opcode,
-                             out_iov, out_num);
-            goto check_session;
+            sreq->cb = virtio_crypto_create_session_completion;
+            ret = virtio_crypto_create_sym_session(vcrypto,
+                            &ctrl.u.sym_create_session,
+                            queue_id, opcode,
+                            out_iov, out_num,
+                            sreq);
+            if (ret < 0) {
+                virtio_crypto_create_session_completion(sreq, ret);
+            }
+            break;
 
         case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
-            session_id = virtio_crypto_create_asym_session(vcrypto,
+            sreq->cb = virtio_crypto_create_session_completion;
+            ret = virtio_crypto_create_asym_session(vcrypto,
                              &ctrl.u.akcipher_create_session,
                              queue_id, opcode,
-                             out_iov, out_num);
-
-check_session:
-            /* Serious errors, need to reset virtio crypto device */
-            if (session_id == -EFAULT) {
-                virtqueue_detach_element(vq, elem, 0);
-                break;
-            } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) {
-                stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
-            } else if (session_id == -VIRTIO_CRYPTO_ERR) {
-                stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
-            } else {
-                /* Set the session id */
-                stq_le_p(&input.session_id, session_id);
-                stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
-            }
-
-            s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
-            if (unlikely(s != sizeof(input))) {
-                virtio_error(vdev, "virtio-crypto input incorrect");
-                virtqueue_detach_element(vq, elem, 0);
-                break;
+                             out_iov, out_num,
+                             sreq);
+            if (ret < 0) {
+                virtio_crypto_create_session_completion(sreq, ret);
             }
-            virtqueue_push(vq, elem, sizeof(input));
-            virtio_notify(vdev, vq);
             break;
 
         case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
@@ -372,37 +420,36 @@ check_session:
         case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
         case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
         case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
-            status = virtio_crypto_handle_close_session(vcrypto,
-                   &ctrl.u.destroy_session, queue_id);
-            /* The status only occupy one byte, we can directly use it */
-            s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
-            if (unlikely(s != sizeof(status))) {
-                virtio_error(vdev, "virtio-crypto status incorrect");
-                virtqueue_detach_element(vq, elem, 0);
-                break;
+            sreq->cb = virtio_crypto_destroy_session_completion;
+            ret = virtio_crypto_handle_close_session(vcrypto,
+                   &ctrl.u.destroy_session, queue_id,
+                   sreq);
+            if (ret < 0) {
+                virtio_crypto_destroy_session_completion(sreq, ret);
             }
-            virtqueue_push(vq, elem, sizeof(status));
-            virtio_notify(vdev, vq);
             break;
+
         case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
         case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
         case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
         default:
+            memset(&input, 0, sizeof(input));
             error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
             stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
             s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
             if (unlikely(s != sizeof(input))) {
                 virtio_error(vdev, "virtio-crypto input incorrect");
                 virtqueue_detach_element(vq, elem, 0);
-                break;
+            } else {
+                virtqueue_push(vq, elem, sizeof(input));
+                virtio_notify(vdev, vq);
             }
-            virtqueue_push(vq, elem, sizeof(input));
-            virtio_notify(vdev, vq);
+            g_free(sreq);
+            g_free(elem);
 
             break;
         } /* end switch case */
 
-        g_free(elem);
     } /* end for loop */
 }
 
@@ -513,11 +560,13 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
     req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
 }
 
-
-static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
+static void virtio_crypto_req_complete(void *opaque, int ret)
 {
+    VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque;
     VirtIOCrypto *vcrypto = req->vcrypto;
     VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+    uint8_t status = -ret;
+    g_autofree struct iovec *in_iov_copy = req->in_iov;
 
     if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
         virtio_crypto_sym_input_data_helper(vdev, req, status,
@@ -529,6 +578,7 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
     stb_p(&req->in->status, status);
     virtqueue_push(req->vq, &req->elem, req->in_len);
     virtio_notify(vdev, req->vq);
+    virtio_crypto_free_request(req);
 }
 
 static VirtIOCryptoReq *
@@ -773,9 +823,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
     unsigned in_num;
     unsigned out_num;
     uint32_t opcode;
-    uint8_t status = VIRTIO_CRYPTO_ERR;
     CryptoDevBackendOpInfo *op_info = &request->op_info;
-    Error *local_err = NULL;
 
     if (elem->out_num < 1 || elem->in_num < 1) {
         virtio_error(vdev, "virtio-crypto dataq missing headers");
@@ -815,6 +863,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
      */
     request->in_num = in_num;
     request->in_iov = in_iov;
+    /* now, we free the in_iov_copy inside virtio_crypto_req_complete */
+    in_iov_copy = NULL;
 
     opcode = ldl_le_p(&req.header.opcode);
     op_info->session_id = ldq_le_p(&req.header.session_id);
@@ -844,22 +894,11 @@ check_result:
             return -1;
         } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
             virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
-            virtio_crypto_free_request(request);
         } else {
-
-            /* Set request's parameter */
-            ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
-                                    request, queue_index, &local_err);
-            if (ret < 0) {
-                status = -ret;
-                if (local_err) {
-                    error_report_err(local_err);
-                }
-            } else { /* ret == VIRTIO_CRYPTO_OK */
-                status = ret;
-            }
-            virtio_crypto_req_complete(request, status);
-            virtio_crypto_free_request(request);
+            cryptodev_backend_crypto_operation(vcrypto->cryptodev,
+                                    request, queue_index,
+                                    virtio_crypto_req_complete,
+                                    request);
         }
         break;
 
@@ -871,7 +910,6 @@ check_result:
         error_report("virtio-crypto unsupported dataq opcode: %u",
                      opcode);
         virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
-        virtio_crypto_free_request(request);
     }
 
     return 0;
@@ -1011,7 +1049,7 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp)
         vcrypto->vqs[i].vcrypto = vcrypto;
     }
 
-    vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl);
+    vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl);
     if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) {
         vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY;
     } else {
diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
index 37c3a360fd..32e9f4cf8a 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -113,6 +113,7 @@ typedef struct CryptoDevBackendSessionInfo {
         CryptoDevBackendSymSessionInfo sym_sess_info;
         CryptoDevBackendAsymSessionInfo asym_sess_info;
     } u;
+    uint64_t session_id;
 } CryptoDevBackendSessionInfo;
 
 /**
@@ -188,21 +189,30 @@ typedef struct CryptoDevBackendOpInfo {
     } u;
 } CryptoDevBackendOpInfo;
 
+typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret);
 struct CryptoDevBackendClass {
     ObjectClass parent_class;
 
     void (*init)(CryptoDevBackend *backend, Error **errp);
     void (*cleanup)(CryptoDevBackend *backend, Error **errp);
 
-    int64_t (*create_session)(CryptoDevBackend *backend,
-                       CryptoDevBackendSessionInfo *sess_info,
-                       uint32_t queue_index, Error **errp);
+    int (*create_session)(CryptoDevBackend *backend,
+                          CryptoDevBackendSessionInfo *sess_info,
+                          uint32_t queue_index,
+                          CryptoDevCompletionFunc cb,
+                          void *opaque);
+
     int (*close_session)(CryptoDevBackend *backend,
-                           uint64_t session_id,
-                           uint32_t queue_index, Error **errp);
+                         uint64_t session_id,
+                         uint32_t queue_index,
+                         CryptoDevCompletionFunc cb,
+                         void *opaque);
+
     int (*do_op)(CryptoDevBackend *backend,
-                     CryptoDevBackendOpInfo *op_info,
-                     uint32_t queue_index, Error **errp);
+                 CryptoDevBackendOpInfo *op_info,
+                 uint32_t queue_index,
+                 CryptoDevCompletionFunc cb,
+                 void *opaque);
 };
 
 typedef enum CryptoDevBackendOptionsType {
@@ -303,15 +313,20 @@ void cryptodev_backend_cleanup(
  * @sess_info: parameters needed by session creating
  * @queue_index: queue index of cryptodev backend client
  * @errp: pointer to a NULL-initialized error object
+ * @cb: callback when session create is compeleted
+ * @opaque: parameter passed to callback
  *
- * Create a session for symmetric/symmetric algorithms
+ * Create a session for symmetric/asymmetric algorithms
  *
- * Returns: session id on success, or -1 on error
+ * Returns: 0 for success and cb will be called when creation is completed,
+ * negative value for error, and cb will not be called.
  */
-int64_t cryptodev_backend_create_session(
+int cryptodev_backend_create_session(
            CryptoDevBackend *backend,
            CryptoDevBackendSessionInfo *sess_info,
-           uint32_t queue_index, Error **errp);
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque);
 
 /**
  * cryptodev_backend_close_session:
@@ -319,34 +334,43 @@ int64_t cryptodev_backend_create_session(
  * @session_id: the session id
  * @queue_index: queue index of cryptodev backend client
  * @errp: pointer to a NULL-initialized error object
+ * @cb: callback when session create is compeleted
+ * @opaque: parameter passed to callback
  *
  * Close a session for which was previously
  * created by cryptodev_backend_create_session()
  *
- * Returns: 0 on success, or Negative on error
+ * Returns: 0 for success and cb will be called when creation is completed,
+ * negative value for error, and cb will not be called.
  */
 int cryptodev_backend_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
-           uint32_t queue_index, Error **errp);
+           uint32_t queue_index,
+           CryptoDevCompletionFunc cb,
+           void *opaque);
 
 /**
  * cryptodev_backend_crypto_operation:
  * @backend: the cryptodev backend object
- * @opaque: pointer to a VirtIOCryptoReq object
+ * @opaque1: pointer to a VirtIOCryptoReq object
  * @queue_index: queue index of cryptodev backend client
  * @errp: pointer to a NULL-initialized error object
+ * @cb: callbacks when operation is completed
+ * @opaque2: parameter passed to cb
  *
  * Do crypto operation, such as encryption and
  * decryption
  *
- * Returns: VIRTIO_CRYPTO_OK on success,
- *         or -VIRTIO_CRYPTO_* on error
+ * Returns: 0 for success and cb will be called when creation is completed,
+ * negative value for error, and cb will not be called.
  */
 int cryptodev_backend_crypto_operation(
                  CryptoDevBackend *backend,
-                 void *opaque,
-                 uint32_t queue_index, Error **errp);
+                 void *opaque1,
+                 uint32_t queue_index,
+                 CryptoDevCompletionFunc cb,
+                 void *opaque2);
 
 /**
  * cryptodev_backend_set_used:
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 2/4] crypto: Support DER encodings
  2022-10-08  8:50 [PATCH v2 0/4] Add a new backend for cryptodev Lei He
  2022-10-08  8:50 ` [PATCH v2 1/4] virtio-crypto: Support asynchronous mode Lei He
@ 2022-10-08  8:50 ` Lei He
  2022-10-11  9:31   ` Daniel P. Berrangé
  2022-10-08  8:50 ` [PATCH v2 3/4] crypto: Support export akcipher to pkcs8 Lei He
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Lei He @ 2022-10-08  8:50 UTC (permalink / raw)
  To: berrange, arei.gonglei, mst; +Cc: pizhenwei, qemu-devel, Lei He

Add encoding interfaces for DER encoding:
1. support decoding of 'bit string', 'octet string', 'object id'
and 'context specific tag' for DER encoder.
2. implemented a simple DER encoder.
3. add more testsuits for DER encoder.

Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 crypto/der.c                 | 307 +++++++++++++++++++++++++++++++++++++++----
 crypto/der.h                 | 211 ++++++++++++++++++++++++++++-
 tests/unit/test-crypto-der.c | 126 ++++++++++++++----
 3 files changed, 597 insertions(+), 47 deletions(-)

diff --git a/crypto/der.c b/crypto/der.c
index f877390bbb..dab3fe4f24 100644
--- a/crypto/der.c
+++ b/crypto/der.c
@@ -22,20 +22,93 @@
 #include "qemu/osdep.h"
 #include "crypto/der.h"
 
+typedef struct QCryptoDerEncodeNode {
+    uint8_t tag;
+    struct QCryptoDerEncodeNode *parent;
+    struct QCryptoDerEncodeNode *next;
+    /* for constructed type, data is null */
+    const uint8_t *data;
+    size_t dlen;
+} QCryptoDerEncodeNode;
+
+typedef struct QCryptoEncodeContext {
+    QCryptoDerEncodeNode root;
+    QCryptoDerEncodeNode *current_parent;
+    QCryptoDerEncodeNode *tail;
+} QCryptoEncodeContext;
+
 enum QCryptoDERTypeTag {
     QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
     QCRYPTO_DER_TYPE_TAG_INT = 0x2,
     QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
     QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
-    QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
-    QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
+    QCRYPTO_DER_TYPE_TAG_NULL = 0x5,
+    QCRYPTO_DER_TYPE_TAG_OID = 0x6,
     QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
     QCRYPTO_DER_TYPE_TAG_SET = 0x11,
 };
 
-#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
+enum QCryptoDERTagClass {
+    QCRYPTO_DER_TAG_CLASS_UNIV = 0x0,
+    QCRYPTO_DER_TAG_CLASS_APPL = 0x1,
+    QCRYPTO_DER_TAG_CLASS_CONT = 0x2,
+    QCRYPTO_DER_TAG_CLASS_PRIV = 0x3,
+};
+
+enum QCryptoDERTagEnc {
+    QCRYPTO_DER_TAG_ENC_PRIM = 0x0,
+    QCRYPTO_DER_TAG_ENC_CONS = 0x1,
+};
+
+#define QCRYPTO_DER_TAG_ENC_MASK 0x20
+#define QCRYPTO_DER_TAG_ENC_SHIFT 5
+
+#define QCRYPTO_DER_TAG_CLASS_MASK 0xc0
+#define QCRYPTO_DER_TAG_CLASS_SHIFT 6
+
+#define QCRYPTO_DER_TAG_VAL_MASK 0x1f
 #define QCRYPTO_DER_SHORT_LEN_MASK 0x80
 
+#define QCRYPTO_DER_TAG(class, enc, val)           \
+    (((class) << QCRYPTO_DER_TAG_CLASS_SHIFT) |    \
+     ((enc) << QCRYPTO_DER_TAG_ENC_SHIFT) | (val))
+
+/**
+ * qcrypto_der_encode_length:
+ * @src_len: the length of source data
+ * @dst: distination to save the encoded 'length', if dst is NULL, only compute
+ * the expected buffer size in bytes.
+ * @dst_len: output parameter, indicates how many bytes wrote.
+ *
+ * Encode the 'length' part of TLV tuple.
+ */
+static void qcrypto_der_encode_length(size_t src_len,
+                                      uint8_t *dst, size_t *dst_len)
+{
+    size_t max_length = 0xFF;
+    uint8_t length_bytes = 0, header_byte;
+
+    if (src_len < QCRYPTO_DER_SHORT_LEN_MASK) {
+        header_byte = src_len;
+        *dst_len = 1;
+    } else {
+        for (length_bytes = 1; max_length < src_len; length_bytes++) {
+            max_length = (max_length << 8) + max_length;
+        }
+        header_byte = length_bytes;
+        header_byte |= QCRYPTO_DER_SHORT_LEN_MASK;
+        *dst_len = length_bytes + 1;
+    }
+    if (!dst) {
+        return;
+    }
+    *dst++ = header_byte;
+    /* Bigendian length bytes */
+    for (; length_bytes > 0; length_bytes--) {
+        *dst++ = ((src_len >> (length_bytes - 1) * 8) & 0xFF);
+    }
+}
+
 static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
 {
     return **data;
@@ -150,40 +223,230 @@ static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
     return qcrypto_der_extract_definite_data(data, dlen, cb, ctx, errp);
 }
 
-int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
-                           QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+static int qcrypto_der_decode_tlv(const uint8_t expected_tag,
+                                  const uint8_t **data, size_t *dlen,
+                                  QCryptoDERDecodeCb cb,
+                                  void *ctx, Error **errp)
 {
+    const uint8_t *saved_data = *data;
+    size_t saved_dlen = *dlen;
     uint8_t tag;
+    int data_length;
+
     if (*dlen < 1) {
         error_setg(errp, "Need more data");
         return -1;
     }
     tag = qcrypto_der_cut_byte(data, dlen);
+    if (tag != expected_tag) {
+        error_setg(errp, "Unexpected tag: expected: %u, actual: %u",
+                   expected_tag, tag);
+        goto error;
+    }
 
-    /* INTEGER must encoded in primitive-form */
-    if (tag != QCRYPTO_DER_TYPE_TAG_INT) {
-        error_setg(errp, "Invalid integer type tag: %u", tag);
-        return -1;
+    data_length = qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+    if (data_length < 0) {
+        goto error;
     }
+    return data_length;
 
-    return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+error:
+    *data = saved_data;
+    *dlen = saved_dlen;
+    return -1;
+}
+
+int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
+                           QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+    const uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                        QCRYPTO_DER_TAG_ENC_PRIM,
+                                        QCRYPTO_DER_TYPE_TAG_INT);
+    return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
 }
 
 int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen,
                            QCryptoDERDecodeCb cb, void *ctx, Error **errp)
 {
-    uint8_t tag;
-    if (*dlen < 1) {
-        error_setg(errp, "Need more data");
-        return -1;
-    }
-    tag = qcrypto_der_cut_byte(data, dlen);
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_CONS,
+                                  QCRYPTO_DER_TYPE_TAG_SEQ);
+    return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
 
-    /* SEQUENCE must use constructed form */
-    if (tag != (QCRYPTO_DER_TYPE_TAG_SEQ | QCRYPTO_DER_CONSTRUCTED_MASK)) {
-        error_setg(errp, "Invalid type sequence tag: %u", tag);
-        return -1;
-    }
+int qcrypto_der_decode_octet_str(const uint8_t **data, size_t *dlen,
+                                 QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_OCT_STR);
+    return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_bit_str(const uint8_t **data, size_t *dlen,
+                               QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_BIT_STR);
+    return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_oid(const uint8_t **data, size_t *dlen,
+                           QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_OID);
+    return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_ctx_tag(const uint8_t **data, size_t *dlen, int tag_id,
+                               QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_CONT,
+                                  QCRYPTO_DER_TAG_ENC_CONS,
+                                  tag_id);
+    return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
 
-    return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+static void qcrypto_der_encode_prim(QCryptoEncodeContext *ctx, uint8_t tag,
+                                    const uint8_t *data, size_t dlen)
+{
+    QCryptoDerEncodeNode *node = g_new0(QCryptoDerEncodeNode, 1);
+    size_t nbytes_len;
+
+    node->tag = tag;
+    node->data = data;
+    node->dlen = dlen;
+    node->parent = ctx->current_parent;
+
+    qcrypto_der_encode_length(dlen, NULL, &nbytes_len);
+    /* 1 byte for Tag, nbyte_len for Length, and dlen for Value */
+    node->parent->dlen += 1 + nbytes_len + dlen;
+
+    ctx->tail->next = node;
+    ctx->tail = node;
+}
+
+QCryptoEncodeContext *qcrypto_der_encode_ctx_new(void)
+{
+    QCryptoEncodeContext *ctx = g_new0(QCryptoEncodeContext, 1);
+    ctx->current_parent = &ctx->root;
+    ctx->tail = &ctx->root;
+    return ctx;
+}
+
+static void qcrypto_der_encode_cons_begin(QCryptoEncodeContext *ctx,
+                                          uint8_t tag)
+{
+    QCryptoDerEncodeNode *node = g_new0(QCryptoDerEncodeNode, 1);
+
+    node->tag = tag;
+    node->parent = ctx->current_parent;
+    ctx->current_parent = node;
+    ctx->tail->next = node;
+    ctx->tail = node;
+}
+
+static void qcrypto_der_encode_cons_end(QCryptoEncodeContext *ctx)
+{
+    QCryptoDerEncodeNode *cons_node = ctx->current_parent;
+    size_t nbytes_len;
+
+    qcrypto_der_encode_length(cons_node->dlen, NULL, &nbytes_len);
+    /* 1 byte for Tag, nbyte_len for Length, and dlen for Value */
+    cons_node->parent->dlen += 1 + nbytes_len + cons_node->dlen;
+    ctx->current_parent = cons_node->parent;
+}
+
+void qcrypto_der_encode_seq_begin(QCryptoEncodeContext *ctx)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_CONS,
+                                  QCRYPTO_DER_TYPE_TAG_SEQ);
+    qcrypto_der_encode_cons_begin(ctx, tag);
+}
+
+void qcrypto_der_encode_seq_end(QCryptoEncodeContext *ctx)
+{
+    qcrypto_der_encode_cons_end(ctx);
+}
+
+void qcrypto_der_encode_oid(QCryptoEncodeContext *ctx,
+                            const uint8_t *src, size_t src_len)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_OID);
+    qcrypto_der_encode_prim(ctx, tag, src, src_len);
+}
+
+void qcrypto_der_encode_int(QCryptoEncodeContext *ctx,
+                            const uint8_t *src, size_t src_len)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_INT);
+    qcrypto_der_encode_prim(ctx, tag, src, src_len);
+}
+
+void qcrypto_der_encode_null(QCryptoEncodeContext *ctx)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_NULL);
+    qcrypto_der_encode_prim(ctx, tag, NULL, 0);
+}
+
+void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx,
+                                  const uint8_t *src, size_t src_len)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_OCT_STR);
+    qcrypto_der_encode_prim(ctx, tag, src, src_len);
+}
+
+void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx)
+{
+    uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+                                  QCRYPTO_DER_TAG_ENC_PRIM,
+                                  QCRYPTO_DER_TYPE_TAG_OCT_STR);
+    qcrypto_der_encode_cons_begin(ctx, tag);
+}
+
+void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx)
+{
+    qcrypto_der_encode_cons_end(ctx);
+}
+
+size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx)
+{
+    return ctx->root.dlen;
+}
+
+void qcrypto_der_encode_ctx_flush_and_free(QCryptoEncodeContext *ctx,
+                                           uint8_t *dst)
+{
+    QCryptoDerEncodeNode *node, *prev;
+    size_t len;
+
+    for (prev = &ctx->root;
+         (node = prev->next) && (prev->next = node->next, 1);) {
+        /* Tag */
+        *dst++ = node->tag;
+
+        /* Length */
+        qcrypto_der_encode_length(node->dlen, dst, &len);
+        dst += len;
+
+        /* Value */
+        if (node->data) {
+            memcpy(dst, node->data, node->dlen);
+            dst += node->dlen;
+        }
+        g_free(node);
+    }
+    g_free(ctx);
 }
diff --git a/crypto/der.h b/crypto/der.h
index e3d3aeacdc..0e895bbeec 100644
--- a/crypto/der.h
+++ b/crypto/der.h
@@ -22,6 +22,11 @@
 
 #include "qapi/error.h"
 
+typedef struct QCryptoEncodeContext QCryptoEncodeContext;
+
+/* rsaEncryption: 1.2.840.113549.1.1.1 */
+#define QCRYPTO_OID_rsaEncryption "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"
+
 /* Simple decoder used to parse DER encoded rsa keys. */
 
 /**
@@ -47,14 +52,13 @@ typedef int (*QCryptoDERDecodeCb) (void *opaque, const uint8_t *value,
  * will be set to the rest length of data, if cb is not NULL, must
  * return 0 to make decode success, at last, the length of the data
  * part of the decoded INTEGER will be returned. Otherwise, -1 is
- * returned.
+ * returned and the valued of *data and *dlen keep unchanged.
  */
 int qcrypto_der_decode_int(const uint8_t **data,
                            size_t *dlen,
                            QCryptoDERDecodeCb cb,
                            void *opaque,
                            Error **errp);
-
 /**
  * qcrypto_der_decode_seq:
  *
@@ -70,7 +74,7 @@ int qcrypto_der_decode_int(const uint8_t **data,
  * will be set to the rest length of data, if cb is not NULL, must
  * return 0 to make decode success, at last, the length of the data
  * part of the decoded SEQUENCE will be returned. Otherwise, -1 is
- * returned.
+ * returned and the valued of *data and *dlen keep unchanged.
  */
 int qcrypto_der_decode_seq(const uint8_t **data,
                            size_t *dlen,
@@ -78,4 +82,205 @@ int qcrypto_der_decode_seq(const uint8_t **data,
                            void *opaque,
                            Error **errp);
 
+/**
+ * qcrypto_der_decode_oid:
+ *
+ * Decode OID from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded OID will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_oid(const uint8_t **data,
+                           size_t *dlen,
+                           QCryptoDERDecodeCb cb,
+                           void *opaque,
+                           Error **errp);
+
+/**
+ * qcrypto_der_decode_octet_str:
+ *
+ * Decode OCTET STRING from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded OCTET STRING will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_octet_str(const uint8_t **data,
+                                 size_t *dlen,
+                                 QCryptoDERDecodeCb cb,
+                                 void *opaque,
+                                 Error **errp);
+
+/**
+ * qcrypto_der_decode_bit_str:
+ *
+ * Decode BIT STRING from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded BIT STRING will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_bit_str(const uint8_t **data,
+                               size_t *dlen,
+                               QCryptoDERDecodeCb cb,
+                               void *opaque,
+                               Error **errp);
+
+
+/**
+ * qcrypto_der_decode_ctx_tag:
+ *
+ * Decode context specific tag
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @tag: expected value of context specific tag
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded BIT STRING will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_ctx_tag(const uint8_t **data,
+                               size_t *dlen, int tag_id,
+                               QCryptoDERDecodeCb cb,
+                               void *opaque,
+                               Error **errp);
+
+/**
+ * qcrypto_der_encode_ctx_new:
+ *
+ * Allocate a context used for der encoding.
+ */
+QCryptoEncodeContext *qcrypto_der_encode_ctx_new(void);
+
+/**
+ * qcrypto_der_encode_seq_begin:
+ * @ctx: the encode context.
+ *
+ * Start encoding a SEQUENCE for ctx.
+ *
+ */
+void qcrypto_der_encode_seq_begin(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_seq_begin:
+ * @ctx: the encode context.
+ *
+ * Finish uencoding a SEQUENCE for ctx.
+ *
+ */
+void qcrypto_der_encode_seq_end(QCryptoEncodeContext *ctx);
+
+
+/**
+ * qcrypto_der_encode_oid:
+ * @ctx: the encode context.
+ * @src: the source data of oid, note it should be already encoded, this
+ * function only add tag and length part for it.
+ *
+ * Encode an oid into ctx.
+ */
+void qcrypto_der_encode_oid(QCryptoEncodeContext *ctx,
+                            const uint8_t *src, size_t src_len);
+
+/**
+ * qcrypto_der_encode_int:
+ * @ctx: the encode context.
+ * @src: the source data of integer, note it should be already encoded, this
+ * function only add tag and length part for it.
+ *
+ * Encode an integer into ctx.
+ */
+void qcrypto_der_encode_int(QCryptoEncodeContext *ctx,
+                            const uint8_t *src, size_t src_len);
+
+/**
+ * qcrypto_der_encode_null:
+ * @ctx: the encode context.
+ *
+ * Encode a null into ctx.
+ */
+void qcrypto_der_encode_null(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_octet_str:
+ * @ctx: the encode context.
+ * @src: the source data of the octet string.
+ *
+ * Encode a octet string into ctx.
+ */
+void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx,
+                                  const uint8_t *src, size_t src_len);
+
+/**
+ * qcrypto_der_encode_octet_str_begin:
+ * @ctx: the encode context.
+ *
+ * Start encoding a octet string, All fields between
+ * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end
+ * are encoded as an octet string. This is useful when we need to encode a
+ * encoded SEQUNCE as OCTET STRING.
+ */
+void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_octet_str_end:
+ * @ctx: the encode context.
+ *
+ * Finish encoding a octet string, All fields between
+ * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end
+ * are encoded as an octet string. This is useful when we need to encode a
+ * encoded SEQUNCE as OCTET STRING.
+ */
+void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_ctx_buffer_len:
+ * @ctx: the encode context.
+ *
+ * Compute the expected buffer size to save all encoded things.
+ */
+size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_ctx_flush_and_free:
+ * @ctx: the encode context.
+ * @dst: the distination to save the encoded data, the length of dst should
+ * not less than qcrypto_der_encode_cxt_buffer_len
+ *
+ * Flush all encoded data into dst, then free ctx.
+ */
+void qcrypto_der_encode_ctx_flush_and_free(QCryptoEncodeContext *ctx,
+                                           uint8_t *dst);
+
 #endif  /* QCRYPTO_ASN1_DECODER_H */
diff --git a/tests/unit/test-crypto-der.c b/tests/unit/test-crypto-der.c
index aed0f28d68..d218a7f170 100644
--- a/tests/unit/test-crypto-der.c
+++ b/tests/unit/test-crypto-der.c
@@ -147,13 +147,58 @@ static const uint8_t test_rsa2048_priv_key[] =
     "\x4e\x2f\x4c\xf9\xab\x97\x38\xe4\x20\x32\x32\x96\xc8\x9e\x79\xd3"
     "\x12";
 
+static const uint8_t test_ecdsa_p192_priv_key[] =
+    "\x30\x53"               /* SEQUENCE, offset 0, length 83 */
+    "\x02\x01\x01"           /* INTEGER, offset 2, length 1 */
+    "\x04\x18"               /* OCTET STRING, offset 5, length 24 */
+    "\xcb\xc8\x86\x0e\x66\x3c\xf7\x5a\x44\x13\xb8\xef\xea\x1d\x7b\xa6"
+    "\x1c\xda\xf4\x1b\xc7\x67\x6b\x35"
+    "\xa1\x34"               /* CONTEXT SPECIFIC 1, offset 31, length 52 */
+    "\x03\x32"               /* BIT STRING, offset 33, length 50 */
+    "\x00\x04\xc4\x16\xb3\xff\xac\xd5\x87\x98\xf7\xd9\x45\xfe\xd3\x5c"
+    "\x17\x9d\xb2\x36\x22\xcc\x07\xb3\x6d\x3c\x4e\x04\x5f\xeb\xb6\x52"
+    "\x58\xfb\x36\x10\x52\xb7\x01\x62\x0e\x94\x51\x1d\xe2\xef\x10\x82"
+    "\x88\x78";
+
+static const uint8_t test_ecdsa_p256_priv_key[] =
+    "\x30\x77"              /* SEQUENCE, offset 0, length 119 */
+    "\x02\x01\x01"          /* INTEGER, offset 2, length 1 */
+    "\x04\x20"              /* OCTET STRING, offset 5, length 32 */
+    "\xf6\x92\xdd\x29\x1c\x6e\xef\xb6\xb2\x73\x9f\x40\x1b\xb3\x2a\x28"
+    "\xd2\x37\xd6\x4a\x5b\xe4\x40\x4c\x6a\x95\x99\xfa\xf7\x92\x49\xbe"
+    "\xa0\x0a"              /* CONTEXT SPECIFIC 0, offset 39, length 10 */
+    "\x06\x08"              /* OID, offset 41, length 8 */
+    "\x2a\x86\x48\xce\x3d\x03\x01\x07"
+    "\xa1\x44"              /* CONTEXT SPECIFIC 1, offset 51, length 68 */
+    "\x03\x42"              /* BIT STRING, offset 53, length 66 */
+    "\x00\x04\xed\x42\x9c\x67\x79\xbe\x46\x83\x88\x3e\x8c\xc1\x33\xf3"
+    "\xc3\xf6\x2c\xf3\x13\x6a\x00\xc2\xc9\x3e\x87\x7f\x86\x39\xe6\xae"
+    "\xe3\xb9\xba\x2f\x58\x63\x32\x62\x62\x54\x07\x27\xf9\x5a\x3a\xc7"
+    "\x3a\x6b\x5b\xbc\x0d\x33\xba\xbb\xd4\xa3\xff\x4f\x9e\xdd\xf5\x59"
+    "\xc0\xf6";
+
 #define MAX_CHECKER_COUNT 32
 
+static int qcrypto_wrapped_decode_ctx_tag0(const uint8_t **data, size_t *dlen,
+                                           QCryptoDERDecodeCb cb, void *opaque,
+                                           Error **errp)
+{
+   return qcrypto_der_decode_ctx_tag(data, dlen, 0, cb, opaque, errp);
+}
+
+static int qcrypto_wrapped_decode_ctx_tag1(const uint8_t **data, size_t *dlen,
+                                           QCryptoDERDecodeCb cb, void *opaque,
+                                           Error **errp)
+{
+   return qcrypto_der_decode_ctx_tag(data, dlen, 1, cb, opaque, errp);
+}
+
 typedef struct QCryptoAns1DecoderResultChecker QCryptoAns1DecoderResultChecker;
 struct QCryptoAns1DecoderResultChecker {
     int (*action) (const uint8_t **data, size_t *dlen,
                    QCryptoDERDecodeCb cb, void *opaque, Error **errp);
     QCryptoDERDecodeCb cb;
+    bool constructed;
     const uint8_t *exp_value;
     size_t exp_vlen;
 };
@@ -204,7 +249,7 @@ static void test_ans1(const void *opaque)
         g_assert(checker->action(&c->data, &c->dlen, checker_callback,
                                  (void *)checker, &error_abort)
             == checker->exp_vlen);
-        if (checker->action == qcrypto_der_decode_seq) {
+        if (checker->constructed) {
             ++seq_depth;
             ctx[seq_depth].data = checker->exp_value;
             ctx[seq_depth].dlen = checker->exp_vlen;
@@ -225,25 +270,25 @@ static QCryptoAns1DecoderTestData test_data[] = {
     .test_data = test_rsa512_priv_key,
     .test_data_len = sizeof(test_rsa512_priv_key) - 1,
     .checker = {
-        { qcrypto_der_decode_seq, checker_callback,
+        { qcrypto_der_decode_seq, checker_callback, true,
           test_rsa512_priv_key + 4, 313 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 4 + 2, 1 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 7 + 2, 65 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 74 + 2, 3 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 79 + 2, 64 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 145 + 2, 33 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 180 + 2, 33 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 215 + 2, 32 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 249 + 2, 32 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa512_priv_key + 283 + 2, 32 },
     },
 },
@@ -252,29 +297,66 @@ static QCryptoAns1DecoderTestData test_data[] = {
     .test_data = test_rsa2048_priv_key,
     .test_data_len = sizeof(test_rsa2048_priv_key) - 1,
     .checker = {
-        { qcrypto_der_decode_seq, checker_callback,
+        { qcrypto_der_decode_seq, checker_callback, true,
           test_rsa2048_priv_key + 4, 1190 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 4 + 2, 1 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 7 + 4, 257 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 268 + 2, 3 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 273 + 4, 257 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 534 + 3, 129 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 666 + 3, 129 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 798 + 3, 129 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 930 + 3, 129 },
-        { qcrypto_der_decode_int, checker_callback,
+        { qcrypto_der_decode_int, checker_callback, false,
           test_rsa2048_priv_key + 1062 + 3, 129 },
     },
 },
-
+{
+    .path = "/crypto/der/parse-ecdsa-p192-priv-key",
+    .test_data = test_ecdsa_p192_priv_key,
+    .test_data_len = sizeof(test_ecdsa_p192_priv_key) - 1,
+    .checker = {
+        { qcrypto_der_decode_seq, checker_callback, true,
+          test_ecdsa_p192_priv_key + 2, 83 },
+        { qcrypto_der_decode_int, checker_callback, false,
+          test_ecdsa_p192_priv_key + 2 + 2, 1 },
+        { qcrypto_der_decode_octet_str, checker_callback, false,
+          test_ecdsa_p192_priv_key + 5 + 2, 24 },
+        { qcrypto_wrapped_decode_ctx_tag1, checker_callback, true,
+          test_ecdsa_p192_priv_key + 31 + 2, 52 },
+        { qcrypto_der_decode_bit_str , checker_callback, false,
+          test_ecdsa_p192_priv_key + 33 + 2, 50 },
+    },
+},
+{
+    .path = "/crypto/der/parse-ecdsa-p256-priv-key",
+    .test_data = test_ecdsa_p256_priv_key,
+    .test_data_len = sizeof(test_ecdsa_p256_priv_key) - 1,
+    .checker = {
+        { qcrypto_der_decode_seq, checker_callback, true,
+          test_ecdsa_p256_priv_key + 2, 119 },
+        { qcrypto_der_decode_int, checker_callback, false,
+          test_ecdsa_p256_priv_key + 2 + 2, 1 },
+        { qcrypto_der_decode_octet_str, checker_callback, false,
+          test_ecdsa_p256_priv_key + 5 + 2, 32 },
+        { qcrypto_wrapped_decode_ctx_tag0, checker_callback, true,
+          test_ecdsa_p256_priv_key + 39 + 2, 10 },
+        { qcrypto_der_decode_oid, checker_callback, false,
+          test_ecdsa_p256_priv_key + 41 + 2, 8 },
+        { qcrypto_wrapped_decode_ctx_tag1, checker_callback, true,
+          test_ecdsa_p256_priv_key + 51 + 2, 68 },
+        { qcrypto_der_decode_bit_str , checker_callback, false,
+          test_ecdsa_p256_priv_key + 53 + 2, 66 },
+    },
+},
 };
 
 int main(int argc, char **argv)
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 3/4] crypto: Support export akcipher to pkcs8
  2022-10-08  8:50 [PATCH v2 0/4] Add a new backend for cryptodev Lei He
  2022-10-08  8:50 ` [PATCH v2 1/4] virtio-crypto: Support asynchronous mode Lei He
  2022-10-08  8:50 ` [PATCH v2 2/4] crypto: Support DER encodings Lei He
@ 2022-10-08  8:50 ` Lei He
  2022-10-11  9:33   ` Daniel P. Berrangé
  2022-10-08  8:50 ` [PATCH v2 4/4] cryptodev: Add a lkcf-backend for cryptodev Lei He
  2022-10-24  8:55 ` [PATCH v2 0/4] Add a new backend " Lei He
  4 siblings, 1 reply; 12+ messages in thread
From: Lei He @ 2022-10-08  8:50 UTC (permalink / raw)
  To: berrange, arei.gonglei, mst; +Cc: pizhenwei, qemu-devel, Lei He

crypto: support export RSA private keys with PKCS#8 standard.
So that users can upload this private key to linux kernel.

Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 crypto/akcipher.c         | 18 ++++++++++++++++++
 crypto/rsakey.c           | 42 ++++++++++++++++++++++++++++++++++++++++++
 crypto/rsakey.h           | 11 ++++++++++-
 include/crypto/akcipher.h | 21 +++++++++++++++++++++
 4 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index ad88379c1e..e4bbc6e5f1 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -22,6 +22,8 @@
 #include "qemu/osdep.h"
 #include "crypto/akcipher.h"
 #include "akcipherpriv.h"
+#include "der.h"
+#include "rsakey.h"
 
 #if defined(CONFIG_GCRYPT)
 #include "akcipher-gcrypt.c.inc"
@@ -106,3 +108,19 @@ void qcrypto_akcipher_free(QCryptoAkCipher *akcipher)
 
     drv->free(akcipher);
 }
+
+int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts,
+                                   uint8_t *key, size_t keylen,
+                                   uint8_t **dst, size_t *dst_len,
+                                   Error **errp)
+{
+    switch (opts->alg) {
+    case QCRYPTO_AKCIPHER_ALG_RSA:
+        qcrypto_akcipher_rsakey_export_p8info(key, keylen, dst, dst_len);
+        return 0;
+
+    default:
+        error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+        return -1;
+    }
+}
diff --git a/crypto/rsakey.c b/crypto/rsakey.c
index cc40e072f0..7d6f273aef 100644
--- a/crypto/rsakey.c
+++ b/crypto/rsakey.c
@@ -19,6 +19,8 @@
  *
  */
 
+#include "qemu/osdep.h"
+#include "der.h"
 #include "rsakey.h"
 
 void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
@@ -37,6 +39,46 @@ void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
     g_free(rsa_key);
 }
 
+/**
+ * PKCS#8 private key info for RSA
+ *
+ * PrivateKeyInfo ::= SEQUENCE {
+ * version         INTEGER,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey      OCTET STRING,
+ * attributes      [0] IMPLICIT Attributes OPTIONAL
+ * }
+ */
+void qcrypto_akcipher_rsakey_export_p8info(const uint8_t *key,
+                                           size_t keylen,
+                                           uint8_t **dst,
+                                           size_t *dlen)
+{
+    QCryptoEncodeContext *ctx = qcrypto_der_encode_ctx_new();
+    uint8_t version = 0;
+
+    qcrypto_der_encode_seq_begin(ctx);
+
+    /* version */
+    qcrypto_der_encode_int(ctx, &version, sizeof(version));
+
+    /* algorithm identifier */
+    qcrypto_der_encode_seq_begin(ctx);
+    qcrypto_der_encode_oid(ctx, (uint8_t *)QCRYPTO_OID_rsaEncryption,
+                           sizeof(QCRYPTO_OID_rsaEncryption) - 1);
+    qcrypto_der_encode_null(ctx);
+    qcrypto_der_encode_seq_end(ctx);
+
+    /* RSA private key */
+    qcrypto_der_encode_octet_str(ctx, key, keylen);
+
+    qcrypto_der_encode_seq_end(ctx);
+
+    *dlen = qcrypto_der_encode_ctx_buffer_len(ctx);
+    *dst = g_malloc(*dlen);
+    qcrypto_der_encode_ctx_flush_and_free(ctx, *dst);
+}
+
 #if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
 #include "rsakey-nettle.c.inc"
 #else
diff --git a/crypto/rsakey.h b/crypto/rsakey.h
index 974b76f659..00b3eccec7 100644
--- a/crypto/rsakey.h
+++ b/crypto/rsakey.h
@@ -22,7 +22,6 @@
 #ifndef QCRYPTO_RSAKEY_H
 #define QCRYPTO_RSAKEY_H
 
-#include "qemu/osdep.h"
 #include "qemu/host-utils.h"
 #include "crypto/akcipher.h"
 
@@ -84,6 +83,16 @@ QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
     QCryptoAkCipherKeyType type,
     const uint8_t *key, size_t keylen, Error **errp);
 
+/**
+ * qcrypto_akcipher_rsakey_export_as_p8info:
+ *
+ * Export RSA private key to PKCS#8 private key info.
+ */
+void qcrypto_akcipher_rsakey_export_p8info(const uint8_t *key,
+                                           size_t keylen,
+                                           uint8_t **dst,
+                                           size_t *dlen);
+
 void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey,
diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
index 51f5fa2774..214e58ca47 100644
--- a/include/crypto/akcipher.h
+++ b/include/crypto/akcipher.h
@@ -153,6 +153,27 @@ int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher);
  */
 void qcrypto_akcipher_free(QCryptoAkCipher *akcipher);
 
+/**
+ * qcrypto_akcipher_export_p8info:
+ * @opts: the options of the akcipher to be exported.
+ * @key: the original key of the akcipher to be exported.
+ * @keylen: length of the 'key'
+ * @dst: output parameter, if export succeed, *dst is set to the
+ * PKCS#8 encoded private key, caller MUST free this key with
+ * g_free after use.
+ * @dst_len: output parameter, indicates the length of PKCS#8 encoded
+ * key.
+ *
+ * Export the akcipher into DER encoded pkcs#8 private key info, expects
+ * |key| stores a valid asymmetric PRIVATE key.
+ *
+ * Returns: 0 for succeed, otherwise -1 is returned.
+ */
+int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts,
+                                   uint8_t *key, size_t keylen,
+                                   uint8_t **dst, size_t *dst_len,
+                                   Error **errp);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipher, qcrypto_akcipher_free)
 
 #endif /* QCRYPTO_AKCIPHER_H */
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 4/4] cryptodev: Add a lkcf-backend for cryptodev
  2022-10-08  8:50 [PATCH v2 0/4] Add a new backend for cryptodev Lei He
                   ` (2 preceding siblings ...)
  2022-10-08  8:50 ` [PATCH v2 3/4] crypto: Support export akcipher to pkcs8 Lei He
@ 2022-10-08  8:50 ` Lei He
  2022-10-24  8:55 ` [PATCH v2 0/4] Add a new backend " Lei He
  4 siblings, 0 replies; 12+ messages in thread
From: Lei He @ 2022-10-08  8:50 UTC (permalink / raw)
  To: berrange, arei.gonglei, mst; +Cc: pizhenwei, qemu-devel, Lei He

cryptodev: Added a new type of backend named lkcf-backend for
cryptodev. This backend upload asymmetric keys to linux kernel,
and let kernel do the accelerations if possible.
The lkcf stands for Linux Kernel Cryptography Framework.

Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 backends/cryptodev-lkcf.c  | 645 +++++++++++++++++++++++++++++++++++++++++++++
 backends/meson.build       |   3 +
 include/sysemu/cryptodev.h |   1 +
 qapi/qom.json              |   2 +
 4 files changed, 651 insertions(+)
 create mode 100644 backends/cryptodev-lkcf.c

diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c
new file mode 100644
index 0000000000..133bd706a4
--- /dev/null
+++ b/backends/cryptodev-lkcf.c
@@ -0,0 +1,645 @@
+/*
+ * QEMU Cryptodev backend for QEMU cipher APIs
+ *
+ * Copyright (c) 2022 Bytedance.Inc
+ *
+ * Authors:
+ *    lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/cipher.h"
+#include "crypto/akcipher.h"
+#include "qapi/error.h"
+#include "qemu/main-loop.h"
+#include "qemu/thread.h"
+#include "qemu/error-report.h"
+#include "qemu/queue.h"
+#include "qom/object.h"
+#include "sysemu/cryptodev.h"
+#include "standard-headers/linux/virtio_crypto.h"
+
+#include <keyutils.h>
+#include <sys/eventfd.h>
+
+/**
+ * @TYPE_CRYPTODEV_BACKEND_LKCF:
+ * name of backend that uses linux kernel crypto framework
+ */
+#define TYPE_CRYPTODEV_BACKEND_LKCF "cryptodev-backend-lkcf"
+
+OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendLKCF, CRYPTODEV_BACKEND_LKCF)
+
+#define INVALID_KEY_ID -1
+#define MAX_SESSIONS 256
+#define NR_WORKER_THREAD 64
+
+#define KCTL_KEY_TYPE_PKEY "asymmetric"
+/**
+ * Here the key is uploaded to the thread-keyring of worker thread, at least
+ * util linux-6.0:
+ * 1. process keyring seems to behave unexpectedly if main-thread does not
+ * create the keyring before creating any other thread.
+ * 2. at present, the guest kernel never perform multiple operations on a
+ * session.
+ * 3. it can reduce the load of the main-loop because the key passed by the
+ * guest kernel has been already checked.
+ */
+#define KCTL_KEY_RING KEY_SPEC_THREAD_KEYRING
+
+typedef struct CryptoDevBackendLKCFSession {
+    uint8_t *key;
+    size_t keylen;
+    QCryptoAkCipherKeyType keytype;
+    QCryptoAkCipherOptions akcipher_opts;
+} CryptoDevBackendLKCFSession;
+
+typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF;
+typedef struct CryptoDevLKCFTask CryptoDevLKCFTask;
+struct CryptoDevLKCFTask {
+    CryptoDevBackendLKCFSession *sess;
+    CryptoDevBackendOpInfo *op_info;
+    CryptoDevCompletionFunc cb;
+    void *opaque;
+    int status;
+    CryptoDevBackendLKCF *lkcf;
+    QSIMPLEQ_ENTRY(CryptoDevLKCFTask) queue;
+};
+
+typedef struct CryptoDevBackendLKCF {
+    CryptoDevBackend parent_obj;
+    CryptoDevBackendLKCFSession *sess[MAX_SESSIONS];
+    QSIMPLEQ_HEAD(, CryptoDevLKCFTask) requests;
+    QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses;
+    QemuMutex mutex;
+    QemuCond cond;
+    QemuMutex rsp_mutex;
+
+    /**
+     * There is no async interface for asymmetric keys like AF_ALG sockets,
+     * we don't seem to have better way than create a lots of thread.
+     */
+    QemuThread worker_threads[NR_WORKER_THREAD];
+    bool running;
+    int eventfd;
+} CryptoDevBackendLKCF;
+
+static void *cryptodev_lkcf_worker(void *arg);
+static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
+                                        uint64_t session_id,
+                                        uint32_t queue_index,
+                                        CryptoDevCompletionFunc cb,
+                                        void *opaque);
+
+static void cryptodev_lkcf_handle_response(void *opaque)
+{
+    CryptoDevBackendLKCF *lkcf = (CryptoDevBackendLKCF *)opaque;
+    QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses;
+    CryptoDevLKCFTask *task, *next;
+    eventfd_t nevent;
+
+    QSIMPLEQ_INIT(&responses);
+    eventfd_read(lkcf->eventfd, &nevent);
+
+    qemu_mutex_lock(&lkcf->rsp_mutex);
+    QSIMPLEQ_PREPEND(&responses, &lkcf->responses);
+    qemu_mutex_unlock(&lkcf->rsp_mutex);
+
+    QSIMPLEQ_FOREACH_SAFE(task, &responses, queue, next) {
+        if (task->cb) {
+            task->cb(task->opaque, task->status);
+        }
+        g_free(task);
+    }
+}
+
+static int cryptodev_lkcf_set_op_desc(QCryptoAkCipherOptions *opts,
+                                      char *key_desc,
+                                      size_t desc_len,
+                                      Error **errp)
+{
+    QCryptoAkCipherOptionsRSA *rsa_opt;
+    if (opts->alg != QCRYPTO_AKCIPHER_ALG_RSA) {
+        error_setg(errp, "Unsupported alg: %u", opts->alg);
+        return -1;
+    }
+
+    rsa_opt = &opts->u.rsa;
+    if (rsa_opt->padding_alg == QCRYPTO_RSA_PADDING_ALG_PKCS1) {
+        snprintf(key_desc, desc_len, "enc=%s hash=%s",
+                 QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg),
+                 QCryptoHashAlgorithm_str(rsa_opt->hash_alg));
+
+    } else {
+        snprintf(key_desc, desc_len, "enc=%s",
+                 QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg));
+    }
+    return 0;
+}
+
+static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg,
+                                      int virtio_hash_alg,
+                                      QCryptoAkCipherOptionsRSA *opt,
+                                      Error **errp)
+{
+    if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+        opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+
+        switch (virtio_hash_alg) {
+        case VIRTIO_CRYPTO_RSA_MD5:
+            opt->hash_alg = QCRYPTO_HASH_ALG_MD5;
+            break;
+
+        case VIRTIO_CRYPTO_RSA_SHA1:
+            opt->hash_alg = QCRYPTO_HASH_ALG_SHA1;
+            break;
+
+        case VIRTIO_CRYPTO_RSA_SHA256:
+            opt->hash_alg = QCRYPTO_HASH_ALG_SHA256;
+            break;
+
+        case VIRTIO_CRYPTO_RSA_SHA512:
+            opt->hash_alg = QCRYPTO_HASH_ALG_SHA512;
+            break;
+
+        default:
+            error_setg(errp, "Unsupported rsa hash algo: %d", virtio_hash_alg);
+            return -1;
+        }
+        return 0;
+    }
+
+    if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+        opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+        return 0;
+    }
+
+    error_setg(errp, "Unsupported rsa padding algo: %u", virtio_padding_alg);
+    return -1;
+}
+
+static int cryptodev_lkcf_get_unused_session_index(CryptoDevBackendLKCF *lkcf)
+{
+    size_t i;
+
+    for (i = 0; i < MAX_SESSIONS; i++) {
+        if (lkcf->sess[i] == NULL) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static void cryptodev_lkcf_init(CryptoDevBackend *backend, Error **errp)
+{
+    /* Only support one queue */
+    int queues = backend->conf.peers.queues, i;
+    CryptoDevBackendClient *cc;
+    CryptoDevBackendLKCF *lkcf =
+        CRYPTODEV_BACKEND_LKCF(backend);
+
+    if (queues != 1) {
+        error_setg(errp,
+                   "Only support one queue in cryptodev-builtin backend");
+        return;
+    }
+    lkcf->eventfd = eventfd(0, 0);
+    if (lkcf->eventfd < 0) {
+        error_setg(errp, "Failed to create eventfd: %d", errno);
+        return;
+    }
+
+    cc = cryptodev_backend_new_client("cryptodev-lkcf", NULL);
+    cc->info_str = g_strdup_printf("cryptodev-lkcf0");
+    cc->queue_index = 0;
+    cc->type = CRYPTODEV_BACKEND_TYPE_LKCF;
+    backend->conf.peers.ccs[0] = cc;
+
+    backend->conf.crypto_services =
+        1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
+    backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
+    lkcf->running = true;
+
+    QSIMPLEQ_INIT(&lkcf->requests);
+    QSIMPLEQ_INIT(&lkcf->responses);
+    qemu_mutex_init(&lkcf->mutex);
+    qemu_mutex_init(&lkcf->rsp_mutex);
+    qemu_cond_init(&lkcf->cond);
+    for (i = 0; i < NR_WORKER_THREAD; i++) {
+        qemu_thread_create(&lkcf->worker_threads[i], "lkcf-worker",
+                           cryptodev_lkcf_worker, lkcf, 0);
+    }
+    qemu_set_fd_handler(
+        lkcf->eventfd, cryptodev_lkcf_handle_response, NULL, lkcf);
+    cryptodev_backend_set_ready(backend, true);
+}
+
+static void cryptodev_lkcf_cleanup(CryptoDevBackend *backend, Error **errp)
+{
+    CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend);
+    size_t i;
+    int queues = backend->conf.peers.queues;
+    CryptoDevBackendClient *cc;
+    CryptoDevLKCFTask *task, *next;
+
+    qemu_mutex_lock(&lkcf->mutex);
+    lkcf->running = false;
+    qemu_mutex_unlock(&lkcf->mutex);
+    qemu_cond_broadcast(&lkcf->cond);
+
+    close(lkcf->eventfd);
+    for (i = 0; i < NR_WORKER_THREAD; i++) {
+        qemu_thread_join(&lkcf->worker_threads[i]);
+    }
+
+    QSIMPLEQ_FOREACH_SAFE(task, &lkcf->requests, queue, next) {
+        if (task->cb) {
+            task->cb(task->opaque, task->status);
+        }
+        g_free(task);
+    }
+
+    QSIMPLEQ_FOREACH_SAFE(task, &lkcf->responses, queue, next) {
+        if (task->cb) {
+            task->cb(task->opaque, task->status);
+        }
+        g_free(task);
+    }
+
+    qemu_mutex_destroy(&lkcf->mutex);
+    qemu_cond_destroy(&lkcf->cond);
+    qemu_mutex_destroy(&lkcf->rsp_mutex);
+
+    for (i = 0; i < MAX_SESSIONS; i++) {
+        if (lkcf->sess[i] != NULL) {
+            cryptodev_lkcf_close_session(backend, i, 0, NULL, NULL);
+        }
+    }
+
+    for (i = 0; i < queues; i++) {
+        cc = backend->conf.peers.ccs[i];
+        if (cc) {
+            cryptodev_backend_free_client(cc);
+            backend->conf.peers.ccs[i] = NULL;
+        }
+    }
+
+    cryptodev_backend_set_ready(backend, false);
+}
+
+static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task)
+{
+    CryptoDevBackendLKCFSession *session = task->sess;
+    CryptoDevBackendAsymOpInfo *asym_op_info;
+    bool kick = false;
+    int ret, status, op_code = task->op_info->op_code;
+    size_t p8info_len;
+    g_autofree uint8_t *p8info = NULL;
+    Error *local_error = NULL;
+    key_serial_t key_id = INVALID_KEY_ID;
+    char op_desc[64];
+    g_autoptr(QCryptoAkCipher) akcipher = NULL;
+
+    /**
+     * We only offload private key session:
+     * 1. currently, the Linux kernel can only accept public key wrapped
+     * with X.509 certificates, but unfortunately the cost of making a
+     * ceritificate with public key is too expensive.
+     * 2. generally, public key related compution is fast, just compute it with
+     * thread-pool.
+     */
+    if (session->keytype == QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE) {
+        if (qcrypto_akcipher_export_p8info(&session->akcipher_opts,
+                                           session->key, session->keylen,
+                                           &p8info, &p8info_len,
+                                           &local_error) != 0 ||
+            cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc,
+                                       sizeof(op_desc), &local_error) != 0) {
+            error_report_err(local_error);
+        } else {
+            key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key",
+                             p8info, p8info_len, KCTL_KEY_RING);
+        }
+    }
+
+    if (key_id < 0) {
+        if (!qcrypto_akcipher_supports(&session->akcipher_opts)) {
+            status = -VIRTIO_CRYPTO_NOTSUPP;
+            goto out;
+        }
+        akcipher = qcrypto_akcipher_new(&session->akcipher_opts,
+                                        session->keytype,
+                                        session->key, session->keylen,
+                                        &local_error);
+        if (!akcipher) {
+            status = -VIRTIO_CRYPTO_ERR;
+            goto out;
+        }
+    }
+
+    asym_op_info = task->op_info->u.asym_op_info;
+    switch (op_code) {
+    case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
+        if (key_id >= 0) {
+            ret = keyctl_pkey_encrypt(key_id, op_desc,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len);
+        } else {
+            ret = qcrypto_akcipher_encrypt(akcipher,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len, &local_error);
+        }
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
+        if (key_id >= 0) {
+            ret = keyctl_pkey_decrypt(key_id, op_desc,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len);
+        } else {
+            ret = qcrypto_akcipher_decrypt(akcipher,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len, &local_error);
+        }
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_SIGN:
+        if (key_id >= 0) {
+            ret = keyctl_pkey_sign(key_id, op_desc,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len);
+        } else {
+            ret = qcrypto_akcipher_sign(akcipher,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len, &local_error);
+        }
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
+        if (key_id >= 0) {
+            ret = keyctl_pkey_verify(key_id, op_desc,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len);
+        } else {
+            ret = qcrypto_akcipher_verify(akcipher,
+                asym_op_info->src, asym_op_info->src_len,
+                asym_op_info->dst, asym_op_info->dst_len, &local_error);
+        }
+        break;
+
+    default:
+        error_setg(&local_error, "Unknown opcode: %u", op_code);
+        status = -VIRTIO_CRYPTO_ERR;
+        goto out;
+    }
+
+    if (ret < 0) {
+        if (!local_error) {
+            if (errno != EKEYREJECTED) {
+                error_report("Failed do operation with keyctl: %d", errno);
+            }
+        } else {
+            error_report_err(local_error);
+        }
+        status = op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY ?
+            -VIRTIO_CRYPTO_KEY_REJECTED : -VIRTIO_CRYPTO_ERR;
+    } else {
+        status = VIRTIO_CRYPTO_OK;
+        asym_op_info->dst_len = ret;
+    }
+
+out:
+    if (key_id >= 0) {
+        keyctl_unlink(key_id, KCTL_KEY_RING);
+    }
+    task->status = status;
+
+    qemu_mutex_lock(&task->lkcf->rsp_mutex);
+    if (QSIMPLEQ_EMPTY(&task->lkcf->responses)) {
+        kick = true;
+    }
+    QSIMPLEQ_INSERT_TAIL(&task->lkcf->responses, task, queue);
+    qemu_mutex_unlock(&task->lkcf->rsp_mutex);
+
+    if (kick) {
+        eventfd_write(task->lkcf->eventfd, 1);
+    }
+}
+
+static void *cryptodev_lkcf_worker(void *arg)
+{
+    CryptoDevBackendLKCF *backend = (CryptoDevBackendLKCF *)arg;
+    CryptoDevLKCFTask *task;
+
+    for (;;) {
+        task = NULL;
+        qemu_mutex_lock(&backend->mutex);
+        while (backend->running && QSIMPLEQ_EMPTY(&backend->requests)) {
+            qemu_cond_wait(&backend->cond, &backend->mutex);
+        }
+        if (backend->running) {
+            task = QSIMPLEQ_FIRST(&backend->requests);
+            QSIMPLEQ_REMOVE_HEAD(&backend->requests, queue);
+        }
+        qemu_mutex_unlock(&backend->mutex);
+
+        /* stopped */
+        if (!task) {
+            break;
+        }
+        cryptodev_lkcf_execute_task(task);
+   }
+
+   return NULL;
+}
+
+static int cryptodev_lkcf_operation(
+    CryptoDevBackend *backend,
+    CryptoDevBackendOpInfo *op_info,
+    uint32_t queue_index,
+    CryptoDevCompletionFunc cb,
+    void *opaque)
+{
+    CryptoDevBackendLKCF *lkcf =
+        CRYPTODEV_BACKEND_LKCF(backend);
+    CryptoDevBackendLKCFSession *sess;
+    enum CryptoDevBackendAlgType algtype = op_info->algtype;
+    CryptoDevLKCFTask *task;
+
+    if (op_info->session_id >= MAX_SESSIONS ||
+        lkcf->sess[op_info->session_id] == NULL) {
+        error_report("Cannot find a valid session id: %" PRIu64 "",
+                     op_info->session_id);
+        return -VIRTIO_CRYPTO_INVSESS;
+    }
+
+    sess = lkcf->sess[op_info->session_id];
+    if (algtype != CRYPTODEV_BACKEND_ALG_ASYM) {
+        error_report("algtype not supported: %u", algtype);
+        return -VIRTIO_CRYPTO_NOTSUPP;
+    }
+
+    task = g_new0(CryptoDevLKCFTask, 1);
+    task->op_info = op_info;
+    task->cb = cb;
+    task->opaque = opaque;
+    task->sess = sess;
+    task->lkcf = lkcf;
+    task->status = -VIRTIO_CRYPTO_ERR;
+
+    qemu_mutex_lock(&lkcf->mutex);
+    QSIMPLEQ_INSERT_TAIL(&lkcf->requests, task, queue);
+    qemu_mutex_unlock(&lkcf->mutex);
+    qemu_cond_signal(&lkcf->cond);
+
+    return VIRTIO_CRYPTO_OK;
+}
+
+static int cryptodev_lkcf_create_asym_session(
+    CryptoDevBackendLKCF *lkcf,
+    CryptoDevBackendAsymSessionInfo *sess_info,
+    uint64_t *session_id)
+{
+    Error *local_error = NULL;
+    int index;
+    g_autofree CryptoDevBackendLKCFSession *sess =
+        g_new0(CryptoDevBackendLKCFSession, 1);
+
+    switch (sess_info->algo) {
+    case VIRTIO_CRYPTO_AKCIPHER_RSA:
+        sess->akcipher_opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+        if (cryptodev_lkcf_set_rsa_opt(
+            sess_info->u.rsa.padding_algo, sess_info->u.rsa.hash_algo,
+            &sess->akcipher_opts.u.rsa, &local_error) != 0) {
+            error_report_err(local_error);
+            return -VIRTIO_CRYPTO_ERR;
+        }
+        break;
+
+    default:
+        error_report("Unsupported asym alg %u", sess_info->algo);
+        return -VIRTIO_CRYPTO_NOTSUPP;
+    }
+
+    switch (sess_info->keytype) {
+    case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+        sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+        sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
+        break;
+
+    default:
+        error_report("Unknown akcipher keytype: %u", sess_info->keytype);
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    index = cryptodev_lkcf_get_unused_session_index(lkcf);
+    if (index < 0) {
+        error_report("Total number of sessions created exceeds %u",
+                     MAX_SESSIONS);
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    sess->keylen = sess_info->keylen;
+    sess->key = g_malloc(sess_info->keylen);
+    memcpy(sess->key, sess_info->key, sess_info->keylen);
+
+    lkcf->sess[index] = g_steal_pointer(&sess);
+    *session_id = index;
+
+    return VIRTIO_CRYPTO_OK;
+}
+
+static int cryptodev_lkcf_create_session(
+    CryptoDevBackend *backend,
+    CryptoDevBackendSessionInfo *sess_info,
+    uint32_t queue_index,
+    CryptoDevCompletionFunc cb,
+    void *opaque)
+{
+    CryptoDevBackendAsymSessionInfo *asym_sess_info;
+    CryptoDevBackendLKCF *lkcf =
+        CRYPTODEV_BACKEND_LKCF(backend);
+    int ret;
+
+    switch (sess_info->op_code) {
+    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+        asym_sess_info = &sess_info->u.asym_sess_info;
+        ret = cryptodev_lkcf_create_asym_session(
+            lkcf, asym_sess_info, &sess_info->session_id);
+        break;
+
+    default:
+        ret = -VIRTIO_CRYPTO_NOTSUPP;
+        error_report("Unsupported opcode: %" PRIu32 "",
+                     sess_info->op_code);
+        break;
+    }
+    if (cb) {
+        cb(opaque, ret);
+    }
+    return 0;
+}
+
+static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
+                                        uint64_t session_id,
+                                        uint32_t queue_index,
+                                        CryptoDevCompletionFunc cb,
+                                        void *opaque)
+{
+    CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend);
+    CryptoDevBackendLKCFSession *session;
+
+    assert(session_id < MAX_SESSIONS && lkcf->sess[session_id]);
+    session = lkcf->sess[session_id];
+    lkcf->sess[session_id] = NULL;
+
+    g_free(session->key);
+    g_free(session);
+
+    if (cb) {
+        cb(opaque, VIRTIO_CRYPTO_OK);
+    }
+    return 0;
+}
+
+static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data)
+{
+    CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
+
+    bc->init = cryptodev_lkcf_init;
+    bc->cleanup = cryptodev_lkcf_cleanup;
+    bc->create_session = cryptodev_lkcf_create_session;
+    bc->close_session = cryptodev_lkcf_close_session;
+    bc->do_op = cryptodev_lkcf_operation;
+}
+
+static const TypeInfo cryptodev_builtin_info = {
+    .name = TYPE_CRYPTODEV_BACKEND_LKCF,
+    .parent = TYPE_CRYPTODEV_BACKEND,
+    .class_init = cryptodev_lkcf_class_init,
+    .instance_size = sizeof(CryptoDevBackendLKCF),
+};
+
+static void cryptodev_lkcf_register_types(void)
+{
+    type_register_static(&cryptodev_builtin_info);
+}
+
+type_init(cryptodev_lkcf_register_types);
diff --git a/backends/meson.build b/backends/meson.build
index b1884a88ec..954e658b25 100644
--- a/backends/meson.build
+++ b/backends/meson.build
@@ -12,6 +12,9 @@ softmmu_ss.add([files(
 softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c'))
 softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c'))
 softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c'))
+if keyutils.found()
+    softmmu_ss.add(keyutils, files('cryptodev-lkcf.c'))
+endif
 if have_vhost_user
   softmmu_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c'))
 endif
diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
index 32e9f4cf8a..cf9b3f07fe 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -219,6 +219,7 @@ typedef enum CryptoDevBackendOptionsType {
     CRYPTODEV_BACKEND_TYPE_NONE = 0,
     CRYPTODEV_BACKEND_TYPE_BUILTIN = 1,
     CRYPTODEV_BACKEND_TYPE_VHOST_USER = 2,
+    CRYPTODEV_BACKEND_TYPE_LKCF = 3,
     CRYPTODEV_BACKEND_TYPE__MAX,
 } CryptoDevBackendOptionsType;
 
diff --git a/qapi/qom.json b/qapi/qom.json
index 80dd419b39..cb83497c73 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -850,6 +850,7 @@
     'colo-compare',
     'cryptodev-backend',
     'cryptodev-backend-builtin',
+    'cryptodev-backend-lkcf',
     { 'name': 'cryptodev-vhost-user',
       'if': 'CONFIG_VHOST_CRYPTO' },
     'dbus-vmstate',
@@ -917,6 +918,7 @@
       'colo-compare':               'ColoCompareProperties',
       'cryptodev-backend':          'CryptodevBackendProperties',
       'cryptodev-backend-builtin':  'CryptodevBackendProperties',
+      'cryptodev-backend-lkcf':     'CryptodevBackendProperties',
       'cryptodev-vhost-user':       { 'type': 'CryptodevVhostUserProperties',
                                       'if': 'CONFIG_VHOST_CRYPTO' },
       'dbus-vmstate':               'DBusVMStateProperties',
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/4] crypto: Support DER encodings
  2022-10-08  8:50 ` [PATCH v2 2/4] crypto: Support DER encodings Lei He
@ 2022-10-11  9:31   ` Daniel P. Berrangé
  0 siblings, 0 replies; 12+ messages in thread
From: Daniel P. Berrangé @ 2022-10-11  9:31 UTC (permalink / raw)
  To: Lei He; +Cc: arei.gonglei, mst, pizhenwei, qemu-devel

On Sat, Oct 08, 2022 at 04:50:28PM +0800, Lei He wrote:
> Add encoding interfaces for DER encoding:
> 1. support decoding of 'bit string', 'octet string', 'object id'
> and 'context specific tag' for DER encoder.
> 2. implemented a simple DER encoder.
> 3. add more testsuits for DER encoder.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> ---
>  crypto/der.c                 | 307 +++++++++++++++++++++++++++++++++++++++----
>  crypto/der.h                 | 211 ++++++++++++++++++++++++++++-
>  tests/unit/test-crypto-der.c | 126 ++++++++++++++----
>  3 files changed, 597 insertions(+), 47 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 3/4] crypto: Support export akcipher to pkcs8
  2022-10-08  8:50 ` [PATCH v2 3/4] crypto: Support export akcipher to pkcs8 Lei He
@ 2022-10-11  9:33   ` Daniel P. Berrangé
  0 siblings, 0 replies; 12+ messages in thread
From: Daniel P. Berrangé @ 2022-10-11  9:33 UTC (permalink / raw)
  To: Lei He; +Cc: arei.gonglei, mst, pizhenwei, qemu-devel

On Sat, Oct 08, 2022 at 04:50:29PM +0800, Lei He wrote:
> crypto: support export RSA private keys with PKCS#8 standard.
> So that users can upload this private key to linux kernel.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> ---
>  crypto/akcipher.c         | 18 ++++++++++++++++++
>  crypto/rsakey.c           | 42 ++++++++++++++++++++++++++++++++++++++++++
>  crypto/rsakey.h           | 11 ++++++++++-
>  include/crypto/akcipher.h | 21 +++++++++++++++++++++
>  4 files changed, 91 insertions(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 0/4] Add a new backend for cryptodev
  2022-10-08  8:50 [PATCH v2 0/4] Add a new backend for cryptodev Lei He
                   ` (3 preceding siblings ...)
  2022-10-08  8:50 ` [PATCH v2 4/4] cryptodev: Add a lkcf-backend for cryptodev Lei He
@ 2022-10-24  8:55 ` Lei He
  4 siblings, 0 replies; 12+ messages in thread
From: Lei He @ 2022-10-24  8:55 UTC (permalink / raw)
  To: berrange, arei.gonglei
  Cc: helei.sig11, pizhenwei, qemu-devel, Michael S. Tsirkin

On 2022/10/8 16:50, Lei He wrote:
> v1 --> v2:
> - Fix compile errors when neither 'nettle' nor 'gcrypt' are enabled.
> - Trivial changes to error codes when neither 'nettle' nor 'gcrypt' are
> enabled.
> 

Hi, lei:
   Daniel has reviewed the crypto part of this patch(thanks a lot for 
Daniel), can you help to have a glance at rest codes? Thanks.

Best regards,
Lei He
--
helei.sig11@bytedance.com



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/4] virtio-crypto: Support asynchronous mode
  2022-10-08  8:50 ` [PATCH v2 1/4] virtio-crypto: Support asynchronous mode Lei He
@ 2022-11-01 10:37   ` Michael S. Tsirkin
  2022-11-01 19:51     ` Michael S. Tsirkin
  0 siblings, 1 reply; 12+ messages in thread
From: Michael S. Tsirkin @ 2022-11-01 10:37 UTC (permalink / raw)
  To: Lei He; +Cc: berrange, arei.gonglei, pizhenwei, qemu-devel

On Sat, Oct 08, 2022 at 04:50:27PM +0800, Lei He wrote:
> virtio-crypto: Modify the current interface of virtio-crypto
> device to support asynchronous mode.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> ---
>  backends/cryptodev-builtin.c    |  69 ++++++---
>  backends/cryptodev-vhost-user.c |  51 +++++--
>  backends/cryptodev.c            |  44 +++---
>  hw/virtio/virtio-crypto.c       | 324 ++++++++++++++++++++++------------------
>  include/sysemu/cryptodev.h      |  60 +++++---
>  5 files changed, 336 insertions(+), 212 deletions(-)
> 
> diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
> index 125cbad1d3..cda6ca3b71 100644
> --- a/backends/cryptodev-builtin.c
> +++ b/backends/cryptodev-builtin.c
> @@ -355,42 +355,62 @@ static int cryptodev_builtin_create_akcipher_session(
>      return index;
>  }
>  
> -static int64_t cryptodev_builtin_create_session(
> +static int cryptodev_builtin_create_session(
>             CryptoDevBackend *backend,
>             CryptoDevBackendSessionInfo *sess_info,
> -           uint32_t queue_index, Error **errp)
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque)
>  {
>      CryptoDevBackendBuiltin *builtin =
>                        CRYPTODEV_BACKEND_BUILTIN(backend);
>      CryptoDevBackendSymSessionInfo *sym_sess_info;
>      CryptoDevBackendAsymSessionInfo *asym_sess_info;
> +    int ret, status;
> +    Error *local_error = NULL;
>  
>      switch (sess_info->op_code) {
>      case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>          sym_sess_info = &sess_info->u.sym_sess_info;
> -        return cryptodev_builtin_create_cipher_session(
> -                           builtin, sym_sess_info, errp);
> +        ret = cryptodev_builtin_create_cipher_session(
> +                    builtin, sym_sess_info, &local_error);
> +        break;
>  
>      case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>          asym_sess_info = &sess_info->u.asym_sess_info;
> -        return cryptodev_builtin_create_akcipher_session(
> -                           builtin, asym_sess_info, errp);
> +        ret = cryptodev_builtin_create_akcipher_session(
> +                           builtin, asym_sess_info, &local_error);
> +        break;
>  
>      case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>      case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>      default:
> -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
> +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
>                     sess_info->op_code);
> -        return -1;
> +        return -VIRTIO_CRYPTO_NOTSUPP;
>      }
>  
> -    return -1;
> +    if (local_error) {
> +        error_report_err(local_error);
> +    }
> +    if (ret < 0) {
> +        status = -VIRTIO_CRYPTO_ERR;
> +    } else {
> +        sess_info->session_id = ret;
> +        status = VIRTIO_CRYPTO_OK;
> +    }
> +    if (cb) {
> +        cb(opaque, status);
> +    }
> +    return 0;
>  }
>  
>  static int cryptodev_builtin_close_session(
>             CryptoDevBackend *backend,
>             uint64_t session_id,
> -           uint32_t queue_index, Error **errp)
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque)
>  {
>      CryptoDevBackendBuiltin *builtin =
>                        CRYPTODEV_BACKEND_BUILTIN(backend);
> @@ -407,6 +427,9 @@ static int cryptodev_builtin_close_session(
>  
>      g_free(session);
>      builtin->sessions[session_id] = NULL;
> +    if (cb) {
> +        cb(opaque, VIRTIO_CRYPTO_OK);
> +    }
>      return 0;
>  }
>  
> @@ -506,7 +529,9 @@ static int cryptodev_builtin_asym_operation(
>  static int cryptodev_builtin_operation(
>                   CryptoDevBackend *backend,
>                   CryptoDevBackendOpInfo *op_info,
> -                 uint32_t queue_index, Error **errp)
> +                 uint32_t queue_index,
> +                 CryptoDevCompletionFunc cb,
> +                 void *opaque)
>  {
>      CryptoDevBackendBuiltin *builtin =
>                        CRYPTODEV_BACKEND_BUILTIN(backend);
> @@ -514,11 +539,12 @@ static int cryptodev_builtin_operation(
>      CryptoDevBackendSymOpInfo *sym_op_info;
>      CryptoDevBackendAsymOpInfo *asym_op_info;
>      enum CryptoDevBackendAlgType algtype = op_info->algtype;
> -    int ret = -VIRTIO_CRYPTO_ERR;
> +    int status = -VIRTIO_CRYPTO_ERR;
> +    Error *local_error = NULL;
>  
>      if (op_info->session_id >= MAX_NUM_SESSIONS ||
>                builtin->sessions[op_info->session_id] == NULL) {
> -        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
> +        error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
>                     op_info->session_id);
>          return -VIRTIO_CRYPTO_INVSESS;
>      }
> @@ -526,14 +552,21 @@ static int cryptodev_builtin_operation(
>      sess = builtin->sessions[op_info->session_id];
>      if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
>          sym_op_info = op_info->u.sym_op_info;
> -        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
> +        status = cryptodev_builtin_sym_operation(sess, sym_op_info,
> +                                                 &local_error);
>      } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
>          asym_op_info = op_info->u.asym_op_info;
> -        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
> -                                               asym_op_info, errp);
> +        status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
> +                                                  asym_op_info, &local_error);
>      }
>  
> -    return ret;
> +    if (local_error) {
> +        error_report_err(local_error);
> +    }
> +    if (cb) {
> +        cb(opaque, status);
> +    }
> +    return 0;
>  }
>  
>  static void cryptodev_builtin_cleanup(
> @@ -548,7 +581,7 @@ static void cryptodev_builtin_cleanup(
>  
>      for (i = 0; i < MAX_NUM_SESSIONS; i++) {
>          if (builtin->sessions[i] != NULL) {
> -            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
> +            cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
>          }
>      }
>  
> diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
> index 5443a59153..351b2729e0 100644
> --- a/backends/cryptodev-vhost-user.c
> +++ b/backends/cryptodev-vhost-user.c
> @@ -259,13 +259,18 @@ static int64_t cryptodev_vhost_user_sym_create_session(
>      return -1;
>  }
>  
> -static int64_t cryptodev_vhost_user_create_session(
> +static int cryptodev_vhost_user_create_session(
>             CryptoDevBackend *backend,
>             CryptoDevBackendSessionInfo *sess_info,
> -           uint32_t queue_index, Error **errp)
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque)
>  {
>      uint32_t op_code = sess_info->op_code;
>      CryptoDevBackendSymSessionInfo *sym_sess_info;
> +    int64_t ret;
> +    Error *local_error = NULL;
> +    int status;
>  
>      switch (op_code) {
>      case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> @@ -273,27 +278,42 @@ static int64_t cryptodev_vhost_user_create_session(
>      case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>      case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>          sym_sess_info = &sess_info->u.sym_sess_info;
> -        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
> -                   queue_index, errp);
> +        ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
> +                   queue_index, &local_error);
> +        break;
> +
>      default:
> -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
> +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
>                     sess_info->op_code);
> -        return -1;
> -
> +        return -VIRTIO_CRYPTO_NOTSUPP;
>      }
>  
> -    return -1;
> +    if (local_error) {
> +        error_report_err(local_error);
> +    }
> +    if (ret < 0) {
> +        status = -VIRTIO_CRYPTO_ERR;
> +    } else {
> +        sess_info->session_id = ret;
> +        status = VIRTIO_CRYPTO_OK;
> +    }
> +    if (cb) {
> +        cb(opaque, status);
> +    }
> +    return 0;
>  }
>  
>  static int cryptodev_vhost_user_close_session(
>             CryptoDevBackend *backend,
>             uint64_t session_id,
> -           uint32_t queue_index, Error **errp)
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque)
>  {
>      CryptoDevBackendClient *cc =
>                    backend->conf.peers.ccs[queue_index];
>      CryptoDevBackendVhost *vhost_crypto;
> -    int ret;
> +    int ret = -1, status;
>  
>      vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
>      if (vhost_crypto) {
> @@ -301,12 +321,17 @@ static int cryptodev_vhost_user_close_session(
>          ret = dev->vhost_ops->vhost_crypto_close_session(dev,
>                                                           session_id);
>          if (ret < 0) {
> -            return -1;
> +            status = -VIRTIO_CRYPTO_ERR;
>          } else {
> -            return 0;
> +            status = VIRTIO_CRYPTO_OK;
>          }
> +    } else {
> +        status = -VIRTIO_CRYPTO_NOTSUPP;
>      }
> -    return -1;
> +    if (cb) {
> +        cb(opaque, status);
> +    }
> +    return 0;
>  }
>  
>  static void cryptodev_vhost_user_cleanup(
> diff --git a/backends/cryptodev.c b/backends/cryptodev.c
> index 33eb4e1a70..54ee8c81f5 100644
> --- a/backends/cryptodev.c
> +++ b/backends/cryptodev.c
> @@ -26,6 +26,7 @@
>  #include "qapi/error.h"
>  #include "qapi/visitor.h"
>  #include "qemu/config-file.h"
> +#include "qemu/error-report.h"
>  #include "qom/object_interfaces.h"
>  #include "hw/virtio/virtio-crypto.h"
>  
> @@ -72,69 +73,72 @@ void cryptodev_backend_cleanup(
>      }
>  }
>  
> -int64_t cryptodev_backend_create_session(
> +int cryptodev_backend_create_session(
>             CryptoDevBackend *backend,
>             CryptoDevBackendSessionInfo *sess_info,
> -           uint32_t queue_index, Error **errp)
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque)
>  {
>      CryptoDevBackendClass *bc =
>                        CRYPTODEV_BACKEND_GET_CLASS(backend);
>  
>      if (bc->create_session) {
> -        return bc->create_session(backend, sess_info, queue_index, errp);
> +        return bc->create_session(backend, sess_info, queue_index, cb, opaque);
>      }
> -
> -    return -1;
> +    return -VIRTIO_CRYPTO_NOTSUPP;
>  }
>  
>  int cryptodev_backend_close_session(
>             CryptoDevBackend *backend,
>             uint64_t session_id,
> -           uint32_t queue_index, Error **errp)
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque)
>  {
>      CryptoDevBackendClass *bc =
>                        CRYPTODEV_BACKEND_GET_CLASS(backend);
>  
>      if (bc->close_session) {
> -        return bc->close_session(backend, session_id, queue_index, errp);
> +        return bc->close_session(backend, session_id, queue_index, cb, opaque);
>      }
> -
> -    return -1;
> +    return -VIRTIO_CRYPTO_NOTSUPP;
>  }
>  
>  static int cryptodev_backend_operation(
>                   CryptoDevBackend *backend,
>                   CryptoDevBackendOpInfo *op_info,
> -                 uint32_t queue_index, Error **errp)
> +                 uint32_t queue_index,
> +                 CryptoDevCompletionFunc cb,
> +                 void *opaque)
>  {
>      CryptoDevBackendClass *bc =
>                        CRYPTODEV_BACKEND_GET_CLASS(backend);
>  
>      if (bc->do_op) {
> -        return bc->do_op(backend, op_info, queue_index, errp);
> +        return bc->do_op(backend, op_info, queue_index, cb, opaque);
>      }
> -
> -    return -VIRTIO_CRYPTO_ERR;
> +    return -VIRTIO_CRYPTO_NOTSUPP;
>  }
>  
>  int cryptodev_backend_crypto_operation(
>                   CryptoDevBackend *backend,
> -                 void *opaque,
> -                 uint32_t queue_index, Error **errp)
> +                 void *opaque1,
> +                 uint32_t queue_index,
> +                 CryptoDevCompletionFunc cb, void *opaque2)
>  {
> -    VirtIOCryptoReq *req = opaque;
> +    VirtIOCryptoReq *req = opaque1;
>      CryptoDevBackendOpInfo *op_info = &req->op_info;
>      enum CryptoDevBackendAlgType algtype = req->flags;
>  
>      if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
>          && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
> -        error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
> -                   algtype);
> -
> +        error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype);
>          return -VIRTIO_CRYPTO_NOTSUPP;
>      }
>  
> -    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
> +    return cryptodev_backend_operation(backend, op_info, queue_index,
> +                                       cb, opaque2);
>  }
>  
>  static void
> diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
> index df4bde210b..39c8f5914e 100644
> --- a/hw/virtio/virtio-crypto.c
> +++ b/hw/virtio/virtio-crypto.c
> @@ -27,6 +27,39 @@
>  
>  #define VIRTIO_CRYPTO_VM_VERSION 1
>  
> +typedef struct VirtIOCryptoSessionReq {
> +    VirtIODevice *vdev;
> +    VirtQueue *vq;
> +    VirtQueueElement *elem;
> +    CryptoDevBackendSessionInfo info;
> +    CryptoDevCompletionFunc cb;
> +} VirtIOCryptoSessionReq;
> +
> +static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq)
> +{
> +    switch (sreq->info.op_code) {
> +    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> +        g_free(sreq->info.u.sym_sess_info.cipher_key);
> +        g_free(sreq->info.u.sym_sess_info.auth_key);
> +        break;
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> +        g_free(sreq->info.u.asym_sess_info.key);
> +        break;
> +
> +    case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
> +    case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
> +    case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
> +    case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
> +    case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
> +        break;
> +
> +    default:
> +        error_report("Unknown opcode: %u", sreq->info.op_code);
> +    }
> +    g_free(sreq);
> +}
> +
>  /*
>   * Transfer virtqueue index to crypto queue index.
>   * The control virtqueue is after the data virtqueues
> @@ -75,27 +108,24 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
>      return 0;
>  }
>  
> -static int64_t
> +static int
>  virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>                 struct virtio_crypto_sym_create_session_req *sess_req,
>                 uint32_t queue_id,
>                 uint32_t opcode,
> -               struct iovec *iov, unsigned int out_num)
> +               struct iovec *iov, unsigned int out_num,
> +               VirtIOCryptoSessionReq *sreq)
>  {
>      VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> -    CryptoDevBackendSessionInfo info;
> -    CryptoDevBackendSymSessionInfo *sym_info;
> -    int64_t session_id;
> +    CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info;
>      int queue_index;
>      uint32_t op_type;
> -    Error *local_err = NULL;
>      int ret;
>  
> -    memset(&info, 0, sizeof(info));
>      op_type = ldl_le_p(&sess_req->op_type);
> -    info.op_code = opcode;
> +    sreq->info.op_code = opcode;
>  
> -    sym_info = &info.u.sym_sess_info;
> +    sym_info = &sreq->info.u.sym_sess_info;
>      sym_info->op_type = op_type;
>  
>      if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
> @@ -103,7 +133,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>                             &sess_req->u.cipher.para,
>                             &iov, &out_num);
>          if (ret < 0) {
> -            goto err;
> +            return ret;
>          }
>      } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
>          size_t s;
> @@ -112,7 +142,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>                             &sess_req->u.chain.para.cipher_param,
>                             &iov, &out_num);
>          if (ret < 0) {
> -            goto err;
> +            return ret;
>          }
>          /* hash part */
>          sym_info->alg_chain_order = ldl_le_p(
> @@ -129,8 +159,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>              if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
>                  error_report("virtio-crypto length of auth key is too big: %u",
>                               sym_info->auth_key_len);
> -                ret = -VIRTIO_CRYPTO_ERR;
> -                goto err;
> +                return -VIRTIO_CRYPTO_ERR;
>              }
>              /* get auth key */
>              if (sym_info->auth_key_len > 0) {
> @@ -140,8 +169,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>                  if (unlikely(s != sym_info->auth_key_len)) {
>                      virtio_error(vdev,
>                            "virtio-crypto authenticated key incorrect");
> -                    ret = -EFAULT;
> -                    goto err;
> +                    return -EFAULT;
>                  }
>                  iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
>              }
> @@ -153,49 +181,30 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>          } else {
>              /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
>              error_report("unsupported hash mode");
> -            ret = -VIRTIO_CRYPTO_NOTSUPP;
> -            goto err;
> +            return -VIRTIO_CRYPTO_NOTSUPP;
>          }
>      } else {
>          /* VIRTIO_CRYPTO_SYM_OP_NONE */
>          error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE");
> -        ret = -VIRTIO_CRYPTO_NOTSUPP;
> -        goto err;
> +        return -VIRTIO_CRYPTO_NOTSUPP;
>      }
>  
>      queue_index = virtio_crypto_vq2q(queue_id);
> -    session_id = cryptodev_backend_create_session(
> -                                     vcrypto->cryptodev,
> -                                     &info, queue_index, &local_err);
> -    if (session_id >= 0) {
> -        ret = session_id;
> -    } else {
> -        if (local_err) {
> -            error_report_err(local_err);
> -        }
> -        ret = -VIRTIO_CRYPTO_ERR;
> -    }
> -
> -err:
> -    g_free(sym_info->cipher_key);
> -    g_free(sym_info->auth_key);
> -    return ret;
> +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
> +                                            queue_index, sreq->cb, sreq);
>  }
>  
> -static int64_t
> +static int
>  virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>                 struct virtio_crypto_akcipher_create_session_req *sess_req,
>                 uint32_t queue_id, uint32_t opcode,
> -               struct iovec *iov, unsigned int out_num)
> +               struct iovec *iov, unsigned int out_num,
> +               VirtIOCryptoSessionReq *sreq)
>  {
>      VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> -    CryptoDevBackendSessionInfo info = {0};
> -    CryptoDevBackendAsymSessionInfo *asym_info;
> -    int64_t session_id;
> +    CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info;
>      int queue_index;
>      uint32_t algo, keytype, keylen;
> -    g_autofree uint8_t *key = NULL;
> -    Error *local_err = NULL;
>  
>      algo = ldl_le_p(&sess_req->para.algo);
>      keytype = ldl_le_p(&sess_req->para.keytype);
> @@ -208,20 +217,19 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>      }
>  
>      if (keylen) {
> -        key = g_malloc(keylen);
> -        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
> +        asym_info->key = g_malloc(keylen);
> +        if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
>              virtio_error(vdev, "virtio-crypto asym key incorrect");
>              return -EFAULT;
>          }
>          iov_discard_front(&iov, &out_num, keylen);
>      }
>  
> -    info.op_code = opcode;
> -    asym_info = &info.u.asym_sess_info;
> +    sreq->info.op_code = opcode;
> +    asym_info = &sreq->info.u.asym_sess_info;
>      asym_info->algo = algo;
>      asym_info->keytype = keytype;
>      asym_info->keylen = keylen;
> -    asym_info->key = key;
>      switch (asym_info->algo) {
>      case VIRTIO_CRYPTO_AKCIPHER_RSA:
>          asym_info->u.rsa.padding_algo =
> @@ -237,45 +245,95 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>      }
>  
>      queue_index = virtio_crypto_vq2q(queue_id);
> -    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
> -                     queue_index, &local_err);
> -    if (session_id < 0) {
> -        if (local_err) {
> -            error_report_err(local_err);
> -        }
> -        return -VIRTIO_CRYPTO_ERR;
> -    }
> -
> -    return session_id;
> +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
> +                                            queue_index, sreq->cb, sreq);
>  }
>  
> -static uint8_t
> +static int
>  virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
>           struct virtio_crypto_destroy_session_req *close_sess_req,
> -         uint32_t queue_id)
> +         uint32_t queue_id,
> +         VirtIOCryptoSessionReq *sreq)
>  {
> -    int ret;
>      uint64_t session_id;
> -    uint32_t status;
> -    Error *local_err = NULL;
>  
>      session_id = ldq_le_p(&close_sess_req->session_id);
>      DPRINTF("close session, id=%" PRIu64 "\n", session_id);
>  
> -    ret = cryptodev_backend_close_session(
> -              vcrypto->cryptodev, session_id, queue_id, &local_err);
> -    if (ret == 0) {
> -        status = VIRTIO_CRYPTO_OK;
> +    return cryptodev_backend_close_session(
> +                vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq);
> +}
> +
> +static void virtio_crypto_create_session_completion(void *opaque, int ret)
> +{
> +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
> +    VirtQueue *vq = sreq->vq;
> +    VirtQueueElement *elem = sreq->elem;
> +    VirtIODevice *vdev = sreq->vdev;
> +    struct virtio_crypto_session_input input;
> +    struct iovec *in_iov = elem->in_sg;
> +    unsigned in_num = elem->in_num;
> +    size_t s;
> +
> +    memset(&input, 0, sizeof(input));
> +    /* Serious errors, need to reset virtio crypto device */
> +    if (ret == -EFAULT) {
> +        virtqueue_detach_element(vq, elem, 0);
> +        goto out;
> +    } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
> +        stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
> +    } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) {
> +        stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED);
> +    } else if (ret != VIRTIO_CRYPTO_OK) {
> +        stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
>      } else {
> -        if (local_err) {
> -            error_report_err(local_err);
> -        } else {
> -            error_report("destroy session failed");
> -        }
> +        /* Set the session id */
> +        stq_le_p(&input.session_id, sreq->info.session_id);
> +        stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
> +    }
> +
> +    s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
> +    if (unlikely(s != sizeof(input))) {
> +        virtio_error(vdev, "virtio-crypto input incorrect");
> +        virtqueue_detach_element(vq, elem, 0);
> +        goto out;
> +    }
> +    virtqueue_push(vq, elem, sizeof(input));
> +    virtio_notify(vdev, vq);
> +
> +out:
> +    g_free(elem);
> +    virtio_crypto_free_create_session_req(sreq);
> +}
> +
> +static void virtio_crypto_destroy_session_completion(void *opaque, int ret)
> +{
> +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
> +    VirtQueue *vq = sreq->vq;
> +    VirtQueueElement *elem = sreq->elem;
> +    VirtIODevice *vdev = sreq->vdev;
> +    struct iovec *in_iov = elem->in_sg;
> +    unsigned in_num = elem->in_num;
> +    uint8_t status;
> +    size_t s;
> +
> +    if (ret < 0) {
>          status = VIRTIO_CRYPTO_ERR;
> +    } else {
> +        status = VIRTIO_CRYPTO_OK;
> +    }
> +    s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
> +    if (unlikely(s != sizeof(status))) {
> +        virtio_error(vdev, "virtio-crypto status incorrect");
> +        virtqueue_detach_element(vq, elem, 0);
> +        goto out;
>      }
> +    virtqueue_push(vq, elem, sizeof(status));
> +    virtio_notify(vdev, vq);
>  
> -    return status;
> +out:
> +    g_free(elem);
> +    g_free(sreq);
>  }
>  
>  static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
> @@ -283,16 +341,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>      VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
>      struct virtio_crypto_op_ctrl_req ctrl;
>      VirtQueueElement *elem;
> -    struct iovec *in_iov;
> -    struct iovec *out_iov;
> -    unsigned in_num;
> +    VirtIOCryptoSessionReq *sreq;
>      unsigned out_num;
> +    unsigned in_num;
>      uint32_t queue_id;
>      uint32_t opcode;
>      struct virtio_crypto_session_input input;
> -    int64_t session_id;
> -    uint8_t status;
>      size_t s;
> +    int ret;
> +    struct iovec *out_iov;
> +    struct iovec *in_iov;
>  
>      for (;;) {
>          g_autofree struct iovec *out_iov_copy = NULL;
> @@ -327,44 +385,34 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>          opcode = ldl_le_p(&ctrl.header.opcode);
>          queue_id = ldl_le_p(&ctrl.header.queue_id);
>  
> -        memset(&input, 0, sizeof(input));
> +        sreq = g_new0(VirtIOCryptoSessionReq, 1);
> +        sreq->vdev = vdev;
> +        sreq->vq = vq;
> +        sreq->elem = elem;
> +
>          switch (opcode) {
>          case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> -            session_id = virtio_crypto_create_sym_session(vcrypto,
> -                             &ctrl.u.sym_create_session,
> -                             queue_id, opcode,
> -                             out_iov, out_num);
> -            goto check_session;
> +            sreq->cb = virtio_crypto_create_session_completion;
> +            ret = virtio_crypto_create_sym_session(vcrypto,
> +                            &ctrl.u.sym_create_session,
> +                            queue_id, opcode,
> +                            out_iov, out_num,
> +                            sreq);
> +            if (ret < 0) {
> +                virtio_crypto_create_session_completion(sreq, ret);
> +            }
> +            break;
>  
>          case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> -            session_id = virtio_crypto_create_asym_session(vcrypto,
> +            sreq->cb = virtio_crypto_create_session_completion;
> +            ret = virtio_crypto_create_asym_session(vcrypto,
>                               &ctrl.u.akcipher_create_session,
>                               queue_id, opcode,
> -                             out_iov, out_num);
> -
> -check_session:
> -            /* Serious errors, need to reset virtio crypto device */
> -            if (session_id == -EFAULT) {
> -                virtqueue_detach_element(vq, elem, 0);
> -                break;
> -            } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) {
> -                stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
> -            } else if (session_id == -VIRTIO_CRYPTO_ERR) {
> -                stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
> -            } else {
> -                /* Set the session id */
> -                stq_le_p(&input.session_id, session_id);
> -                stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
> -            }
> -
> -            s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
> -            if (unlikely(s != sizeof(input))) {
> -                virtio_error(vdev, "virtio-crypto input incorrect");
> -                virtqueue_detach_element(vq, elem, 0);
> -                break;
> +                             out_iov, out_num,
> +                             sreq);
> +            if (ret < 0) {
> +                virtio_crypto_create_session_completion(sreq, ret);
>              }
> -            virtqueue_push(vq, elem, sizeof(input));
> -            virtio_notify(vdev, vq);
>              break;
>  
>          case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
> @@ -372,37 +420,36 @@ check_session:
>          case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
>          case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
>          case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
> -            status = virtio_crypto_handle_close_session(vcrypto,
> -                   &ctrl.u.destroy_session, queue_id);
> -            /* The status only occupy one byte, we can directly use it */
> -            s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
> -            if (unlikely(s != sizeof(status))) {
> -                virtio_error(vdev, "virtio-crypto status incorrect");
> -                virtqueue_detach_element(vq, elem, 0);
> -                break;
> +            sreq->cb = virtio_crypto_destroy_session_completion;
> +            ret = virtio_crypto_handle_close_session(vcrypto,
> +                   &ctrl.u.destroy_session, queue_id,
> +                   sreq);
> +            if (ret < 0) {
> +                virtio_crypto_destroy_session_completion(sreq, ret);
>              }
> -            virtqueue_push(vq, elem, sizeof(status));
> -            virtio_notify(vdev, vq);
>              break;
> +
>          case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>          case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>          case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>          default:
> +            memset(&input, 0, sizeof(input));
>              error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
>              stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>              s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>              if (unlikely(s != sizeof(input))) {
>                  virtio_error(vdev, "virtio-crypto input incorrect");
>                  virtqueue_detach_element(vq, elem, 0);
> -                break;
> +            } else {
> +                virtqueue_push(vq, elem, sizeof(input));
> +                virtio_notify(vdev, vq);
>              }
> -            virtqueue_push(vq, elem, sizeof(input));
> -            virtio_notify(vdev, vq);
> +            g_free(sreq);
> +            g_free(elem);
>  
>              break;
>          } /* end switch case */
>  
> -        g_free(elem);
>      } /* end for loop */
>  }
>  
> @@ -513,11 +560,13 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
>      req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
>  }
>  
> -
> -static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
> +static void virtio_crypto_req_complete(void *opaque, int ret)
>  {
> +    VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque;
>      VirtIOCrypto *vcrypto = req->vcrypto;
>      VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> +    uint8_t status = -ret;
> +    g_autofree struct iovec *in_iov_copy = req->in_iov;
>  
>      if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
>          virtio_crypto_sym_input_data_helper(vdev, req, status,

I am not sure why is this g_autofree here.
The variable is unused and clang gets confused (a bug in
clang but oh well).
Is the point to free when this goes out of scope?
I suspect we should just g_free inside virtio_crypto_free_request
or something like this.
Pls send a fixup as otherwise I'll have to defer this until after
the coming release.



> @@ -529,6 +578,7 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>      stb_p(&req->in->status, status);
>      virtqueue_push(req->vq, &req->elem, req->in_len);
>      virtio_notify(vdev, req->vq);
> +    virtio_crypto_free_request(req);
>  }
>  
>  static VirtIOCryptoReq *
> @@ -773,9 +823,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>      unsigned in_num;
>      unsigned out_num;
>      uint32_t opcode;
> -    uint8_t status = VIRTIO_CRYPTO_ERR;
>      CryptoDevBackendOpInfo *op_info = &request->op_info;
> -    Error *local_err = NULL;
>  
>      if (elem->out_num < 1 || elem->in_num < 1) {
>          virtio_error(vdev, "virtio-crypto dataq missing headers");
> @@ -815,6 +863,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>       */
>      request->in_num = in_num;
>      request->in_iov = in_iov;
> +    /* now, we free the in_iov_copy inside virtio_crypto_req_complete */
> +    in_iov_copy = NULL;
>  
>      opcode = ldl_le_p(&req.header.opcode);
>      op_info->session_id = ldq_le_p(&req.header.session_id);
> @@ -844,22 +894,11 @@ check_result:
>              return -1;
>          } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
>              virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
> -            virtio_crypto_free_request(request);
>          } else {
> -
> -            /* Set request's parameter */
> -            ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
> -                                    request, queue_index, &local_err);
> -            if (ret < 0) {
> -                status = -ret;
> -                if (local_err) {
> -                    error_report_err(local_err);
> -                }
> -            } else { /* ret == VIRTIO_CRYPTO_OK */
> -                status = ret;
> -            }
> -            virtio_crypto_req_complete(request, status);
> -            virtio_crypto_free_request(request);
> +            cryptodev_backend_crypto_operation(vcrypto->cryptodev,
> +                                    request, queue_index,
> +                                    virtio_crypto_req_complete,
> +                                    request);
>          }
>          break;
>  
> @@ -871,7 +910,6 @@ check_result:
>          error_report("virtio-crypto unsupported dataq opcode: %u",
>                       opcode);
>          virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
> -        virtio_crypto_free_request(request);
>      }
>  
>      return 0;
> @@ -1011,7 +1049,7 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp)
>          vcrypto->vqs[i].vcrypto = vcrypto;
>      }
>  
> -    vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl);
> +    vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl);
>      if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) {
>          vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY;
>      } else {
> diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
> index 37c3a360fd..32e9f4cf8a 100644
> --- a/include/sysemu/cryptodev.h
> +++ b/include/sysemu/cryptodev.h
> @@ -113,6 +113,7 @@ typedef struct CryptoDevBackendSessionInfo {
>          CryptoDevBackendSymSessionInfo sym_sess_info;
>          CryptoDevBackendAsymSessionInfo asym_sess_info;
>      } u;
> +    uint64_t session_id;
>  } CryptoDevBackendSessionInfo;
>  
>  /**
> @@ -188,21 +189,30 @@ typedef struct CryptoDevBackendOpInfo {
>      } u;
>  } CryptoDevBackendOpInfo;
>  
> +typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret);
>  struct CryptoDevBackendClass {
>      ObjectClass parent_class;
>  
>      void (*init)(CryptoDevBackend *backend, Error **errp);
>      void (*cleanup)(CryptoDevBackend *backend, Error **errp);
>  
> -    int64_t (*create_session)(CryptoDevBackend *backend,
> -                       CryptoDevBackendSessionInfo *sess_info,
> -                       uint32_t queue_index, Error **errp);
> +    int (*create_session)(CryptoDevBackend *backend,
> +                          CryptoDevBackendSessionInfo *sess_info,
> +                          uint32_t queue_index,
> +                          CryptoDevCompletionFunc cb,
> +                          void *opaque);
> +
>      int (*close_session)(CryptoDevBackend *backend,
> -                           uint64_t session_id,
> -                           uint32_t queue_index, Error **errp);
> +                         uint64_t session_id,
> +                         uint32_t queue_index,
> +                         CryptoDevCompletionFunc cb,
> +                         void *opaque);
> +
>      int (*do_op)(CryptoDevBackend *backend,
> -                     CryptoDevBackendOpInfo *op_info,
> -                     uint32_t queue_index, Error **errp);
> +                 CryptoDevBackendOpInfo *op_info,
> +                 uint32_t queue_index,
> +                 CryptoDevCompletionFunc cb,
> +                 void *opaque);
>  };
>  
>  typedef enum CryptoDevBackendOptionsType {
> @@ -303,15 +313,20 @@ void cryptodev_backend_cleanup(
>   * @sess_info: parameters needed by session creating
>   * @queue_index: queue index of cryptodev backend client
>   * @errp: pointer to a NULL-initialized error object
> + * @cb: callback when session create is compeleted
> + * @opaque: parameter passed to callback
>   *
> - * Create a session for symmetric/symmetric algorithms
> + * Create a session for symmetric/asymmetric algorithms
>   *
> - * Returns: session id on success, or -1 on error
> + * Returns: 0 for success and cb will be called when creation is completed,
> + * negative value for error, and cb will not be called.
>   */
> -int64_t cryptodev_backend_create_session(
> +int cryptodev_backend_create_session(
>             CryptoDevBackend *backend,
>             CryptoDevBackendSessionInfo *sess_info,
> -           uint32_t queue_index, Error **errp);
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque);
>  
>  /**
>   * cryptodev_backend_close_session:
> @@ -319,34 +334,43 @@ int64_t cryptodev_backend_create_session(
>   * @session_id: the session id
>   * @queue_index: queue index of cryptodev backend client
>   * @errp: pointer to a NULL-initialized error object
> + * @cb: callback when session create is compeleted
> + * @opaque: parameter passed to callback
>   *
>   * Close a session for which was previously
>   * created by cryptodev_backend_create_session()
>   *
> - * Returns: 0 on success, or Negative on error
> + * Returns: 0 for success and cb will be called when creation is completed,
> + * negative value for error, and cb will not be called.
>   */
>  int cryptodev_backend_close_session(
>             CryptoDevBackend *backend,
>             uint64_t session_id,
> -           uint32_t queue_index, Error **errp);
> +           uint32_t queue_index,
> +           CryptoDevCompletionFunc cb,
> +           void *opaque);
>  
>  /**
>   * cryptodev_backend_crypto_operation:
>   * @backend: the cryptodev backend object
> - * @opaque: pointer to a VirtIOCryptoReq object
> + * @opaque1: pointer to a VirtIOCryptoReq object
>   * @queue_index: queue index of cryptodev backend client
>   * @errp: pointer to a NULL-initialized error object
> + * @cb: callbacks when operation is completed
> + * @opaque2: parameter passed to cb
>   *
>   * Do crypto operation, such as encryption and
>   * decryption
>   *
> - * Returns: VIRTIO_CRYPTO_OK on success,
> - *         or -VIRTIO_CRYPTO_* on error
> + * Returns: 0 for success and cb will be called when creation is completed,
> + * negative value for error, and cb will not be called.
>   */
>  int cryptodev_backend_crypto_operation(
>                   CryptoDevBackend *backend,
> -                 void *opaque,
> -                 uint32_t queue_index, Error **errp);
> +                 void *opaque1,
> +                 uint32_t queue_index,
> +                 CryptoDevCompletionFunc cb,
> +                 void *opaque2);
>  
>  /**
>   * cryptodev_backend_set_used:
> -- 
> 2.11.0



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/4] virtio-crypto: Support asynchronous mode
  2022-11-01 10:37   ` Michael S. Tsirkin
@ 2022-11-01 19:51     ` Michael S. Tsirkin
  2022-11-02  2:24       ` Lei He
  2022-11-02  8:18       ` Lei He
  0 siblings, 2 replies; 12+ messages in thread
From: Michael S. Tsirkin @ 2022-11-01 19:51 UTC (permalink / raw)
  To: Lei He; +Cc: berrange, arei.gonglei, pizhenwei, qemu-devel

On Tue, Nov 01, 2022 at 06:37:26AM -0400, Michael S. Tsirkin wrote:
> On Sat, Oct 08, 2022 at 04:50:27PM +0800, Lei He wrote:
> > virtio-crypto: Modify the current interface of virtio-crypto
> > device to support asynchronous mode.
> > 
> > Signed-off-by: lei he <helei.sig11@bytedance.com>
> > ---
> >  backends/cryptodev-builtin.c    |  69 ++++++---
> >  backends/cryptodev-vhost-user.c |  51 +++++--
> >  backends/cryptodev.c            |  44 +++---
> >  hw/virtio/virtio-crypto.c       | 324 ++++++++++++++++++++++------------------
> >  include/sysemu/cryptodev.h      |  60 +++++---
> >  5 files changed, 336 insertions(+), 212 deletions(-)
> > 
> > diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
> > index 125cbad1d3..cda6ca3b71 100644
> > --- a/backends/cryptodev-builtin.c
> > +++ b/backends/cryptodev-builtin.c
> > @@ -355,42 +355,62 @@ static int cryptodev_builtin_create_akcipher_session(
> >      return index;
> >  }
> >  
> > -static int64_t cryptodev_builtin_create_session(
> > +static int cryptodev_builtin_create_session(
> >             CryptoDevBackend *backend,
> >             CryptoDevBackendSessionInfo *sess_info,
> > -           uint32_t queue_index, Error **errp)
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque)
> >  {
> >      CryptoDevBackendBuiltin *builtin =
> >                        CRYPTODEV_BACKEND_BUILTIN(backend);
> >      CryptoDevBackendSymSessionInfo *sym_sess_info;
> >      CryptoDevBackendAsymSessionInfo *asym_sess_info;
> > +    int ret, status;
> > +    Error *local_error = NULL;
> >  
> >      switch (sess_info->op_code) {
> >      case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> >          sym_sess_info = &sess_info->u.sym_sess_info;
> > -        return cryptodev_builtin_create_cipher_session(
> > -                           builtin, sym_sess_info, errp);
> > +        ret = cryptodev_builtin_create_cipher_session(
> > +                    builtin, sym_sess_info, &local_error);
> > +        break;
> >  
> >      case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> >          asym_sess_info = &sess_info->u.asym_sess_info;
> > -        return cryptodev_builtin_create_akcipher_session(
> > -                           builtin, asym_sess_info, errp);
> > +        ret = cryptodev_builtin_create_akcipher_session(
> > +                           builtin, asym_sess_info, &local_error);
> > +        break;
> >  
> >      case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
> >      case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
> >      default:
> > -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
> > +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
> >                     sess_info->op_code);
> > -        return -1;
> > +        return -VIRTIO_CRYPTO_NOTSUPP;
> >      }
> >  
> > -    return -1;
> > +    if (local_error) {
> > +        error_report_err(local_error);
> > +    }
> > +    if (ret < 0) {
> > +        status = -VIRTIO_CRYPTO_ERR;
> > +    } else {
> > +        sess_info->session_id = ret;
> > +        status = VIRTIO_CRYPTO_OK;
> > +    }
> > +    if (cb) {
> > +        cb(opaque, status);
> > +    }
> > +    return 0;
> >  }
> >  
> >  static int cryptodev_builtin_close_session(
> >             CryptoDevBackend *backend,
> >             uint64_t session_id,
> > -           uint32_t queue_index, Error **errp)
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque)
> >  {
> >      CryptoDevBackendBuiltin *builtin =
> >                        CRYPTODEV_BACKEND_BUILTIN(backend);
> > @@ -407,6 +427,9 @@ static int cryptodev_builtin_close_session(
> >  
> >      g_free(session);
> >      builtin->sessions[session_id] = NULL;
> > +    if (cb) {
> > +        cb(opaque, VIRTIO_CRYPTO_OK);
> > +    }
> >      return 0;
> >  }
> >  
> > @@ -506,7 +529,9 @@ static int cryptodev_builtin_asym_operation(
> >  static int cryptodev_builtin_operation(
> >                   CryptoDevBackend *backend,
> >                   CryptoDevBackendOpInfo *op_info,
> > -                 uint32_t queue_index, Error **errp)
> > +                 uint32_t queue_index,
> > +                 CryptoDevCompletionFunc cb,
> > +                 void *opaque)
> >  {
> >      CryptoDevBackendBuiltin *builtin =
> >                        CRYPTODEV_BACKEND_BUILTIN(backend);
> > @@ -514,11 +539,12 @@ static int cryptodev_builtin_operation(
> >      CryptoDevBackendSymOpInfo *sym_op_info;
> >      CryptoDevBackendAsymOpInfo *asym_op_info;
> >      enum CryptoDevBackendAlgType algtype = op_info->algtype;
> > -    int ret = -VIRTIO_CRYPTO_ERR;
> > +    int status = -VIRTIO_CRYPTO_ERR;
> > +    Error *local_error = NULL;
> >  
> >      if (op_info->session_id >= MAX_NUM_SESSIONS ||
> >                builtin->sessions[op_info->session_id] == NULL) {
> > -        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
> > +        error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
> >                     op_info->session_id);
> >          return -VIRTIO_CRYPTO_INVSESS;
> >      }
> > @@ -526,14 +552,21 @@ static int cryptodev_builtin_operation(
> >      sess = builtin->sessions[op_info->session_id];
> >      if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
> >          sym_op_info = op_info->u.sym_op_info;
> > -        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
> > +        status = cryptodev_builtin_sym_operation(sess, sym_op_info,
> > +                                                 &local_error);
> >      } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
> >          asym_op_info = op_info->u.asym_op_info;
> > -        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
> > -                                               asym_op_info, errp);
> > +        status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
> > +                                                  asym_op_info, &local_error);
> >      }
> >  
> > -    return ret;
> > +    if (local_error) {
> > +        error_report_err(local_error);
> > +    }
> > +    if (cb) {
> > +        cb(opaque, status);
> > +    }
> > +    return 0;
> >  }
> >  
> >  static void cryptodev_builtin_cleanup(
> > @@ -548,7 +581,7 @@ static void cryptodev_builtin_cleanup(
> >  
> >      for (i = 0; i < MAX_NUM_SESSIONS; i++) {
> >          if (builtin->sessions[i] != NULL) {
> > -            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
> > +            cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
> >          }
> >      }
> >  
> > diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
> > index 5443a59153..351b2729e0 100644
> > --- a/backends/cryptodev-vhost-user.c
> > +++ b/backends/cryptodev-vhost-user.c
> > @@ -259,13 +259,18 @@ static int64_t cryptodev_vhost_user_sym_create_session(
> >      return -1;
> >  }
> >  
> > -static int64_t cryptodev_vhost_user_create_session(
> > +static int cryptodev_vhost_user_create_session(
> >             CryptoDevBackend *backend,
> >             CryptoDevBackendSessionInfo *sess_info,
> > -           uint32_t queue_index, Error **errp)
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque)
> >  {
> >      uint32_t op_code = sess_info->op_code;
> >      CryptoDevBackendSymSessionInfo *sym_sess_info;
> > +    int64_t ret;
> > +    Error *local_error = NULL;
> > +    int status;
> >  
> >      switch (op_code) {
> >      case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> > @@ -273,27 +278,42 @@ static int64_t cryptodev_vhost_user_create_session(
> >      case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
> >      case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
> >          sym_sess_info = &sess_info->u.sym_sess_info;
> > -        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
> > -                   queue_index, errp);
> > +        ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
> > +                   queue_index, &local_error);
> > +        break;
> > +
> >      default:
> > -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
> > +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
> >                     sess_info->op_code);
> > -        return -1;
> > -
> > +        return -VIRTIO_CRYPTO_NOTSUPP;
> >      }
> >  
> > -    return -1;
> > +    if (local_error) {
> > +        error_report_err(local_error);
> > +    }
> > +    if (ret < 0) {
> > +        status = -VIRTIO_CRYPTO_ERR;
> > +    } else {
> > +        sess_info->session_id = ret;
> > +        status = VIRTIO_CRYPTO_OK;
> > +    }
> > +    if (cb) {
> > +        cb(opaque, status);
> > +    }
> > +    return 0;
> >  }
> >  
> >  static int cryptodev_vhost_user_close_session(
> >             CryptoDevBackend *backend,
> >             uint64_t session_id,
> > -           uint32_t queue_index, Error **errp)
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque)
> >  {
> >      CryptoDevBackendClient *cc =
> >                    backend->conf.peers.ccs[queue_index];
> >      CryptoDevBackendVhost *vhost_crypto;
> > -    int ret;
> > +    int ret = -1, status;
> >  
> >      vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
> >      if (vhost_crypto) {
> > @@ -301,12 +321,17 @@ static int cryptodev_vhost_user_close_session(
> >          ret = dev->vhost_ops->vhost_crypto_close_session(dev,
> >                                                           session_id);
> >          if (ret < 0) {
> > -            return -1;
> > +            status = -VIRTIO_CRYPTO_ERR;
> >          } else {
> > -            return 0;
> > +            status = VIRTIO_CRYPTO_OK;
> >          }
> > +    } else {
> > +        status = -VIRTIO_CRYPTO_NOTSUPP;
> >      }
> > -    return -1;
> > +    if (cb) {
> > +        cb(opaque, status);
> > +    }
> > +    return 0;
> >  }
> >  
> >  static void cryptodev_vhost_user_cleanup(
> > diff --git a/backends/cryptodev.c b/backends/cryptodev.c
> > index 33eb4e1a70..54ee8c81f5 100644
> > --- a/backends/cryptodev.c
> > +++ b/backends/cryptodev.c
> > @@ -26,6 +26,7 @@
> >  #include "qapi/error.h"
> >  #include "qapi/visitor.h"
> >  #include "qemu/config-file.h"
> > +#include "qemu/error-report.h"
> >  #include "qom/object_interfaces.h"
> >  #include "hw/virtio/virtio-crypto.h"
> >  
> > @@ -72,69 +73,72 @@ void cryptodev_backend_cleanup(
> >      }
> >  }
> >  
> > -int64_t cryptodev_backend_create_session(
> > +int cryptodev_backend_create_session(
> >             CryptoDevBackend *backend,
> >             CryptoDevBackendSessionInfo *sess_info,
> > -           uint32_t queue_index, Error **errp)
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque)
> >  {
> >      CryptoDevBackendClass *bc =
> >                        CRYPTODEV_BACKEND_GET_CLASS(backend);
> >  
> >      if (bc->create_session) {
> > -        return bc->create_session(backend, sess_info, queue_index, errp);
> > +        return bc->create_session(backend, sess_info, queue_index, cb, opaque);
> >      }
> > -
> > -    return -1;
> > +    return -VIRTIO_CRYPTO_NOTSUPP;
> >  }
> >  
> >  int cryptodev_backend_close_session(
> >             CryptoDevBackend *backend,
> >             uint64_t session_id,
> > -           uint32_t queue_index, Error **errp)
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque)
> >  {
> >      CryptoDevBackendClass *bc =
> >                        CRYPTODEV_BACKEND_GET_CLASS(backend);
> >  
> >      if (bc->close_session) {
> > -        return bc->close_session(backend, session_id, queue_index, errp);
> > +        return bc->close_session(backend, session_id, queue_index, cb, opaque);
> >      }
> > -
> > -    return -1;
> > +    return -VIRTIO_CRYPTO_NOTSUPP;
> >  }
> >  
> >  static int cryptodev_backend_operation(
> >                   CryptoDevBackend *backend,
> >                   CryptoDevBackendOpInfo *op_info,
> > -                 uint32_t queue_index, Error **errp)
> > +                 uint32_t queue_index,
> > +                 CryptoDevCompletionFunc cb,
> > +                 void *opaque)
> >  {
> >      CryptoDevBackendClass *bc =
> >                        CRYPTODEV_BACKEND_GET_CLASS(backend);
> >  
> >      if (bc->do_op) {
> > -        return bc->do_op(backend, op_info, queue_index, errp);
> > +        return bc->do_op(backend, op_info, queue_index, cb, opaque);
> >      }
> > -
> > -    return -VIRTIO_CRYPTO_ERR;
> > +    return -VIRTIO_CRYPTO_NOTSUPP;
> >  }
> >  
> >  int cryptodev_backend_crypto_operation(
> >                   CryptoDevBackend *backend,
> > -                 void *opaque,
> > -                 uint32_t queue_index, Error **errp)
> > +                 void *opaque1,
> > +                 uint32_t queue_index,
> > +                 CryptoDevCompletionFunc cb, void *opaque2)
> >  {
> > -    VirtIOCryptoReq *req = opaque;
> > +    VirtIOCryptoReq *req = opaque1;
> >      CryptoDevBackendOpInfo *op_info = &req->op_info;
> >      enum CryptoDevBackendAlgType algtype = req->flags;
> >  
> >      if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
> >          && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
> > -        error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
> > -                   algtype);
> > -
> > +        error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype);
> >          return -VIRTIO_CRYPTO_NOTSUPP;
> >      }
> >  
> > -    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
> > +    return cryptodev_backend_operation(backend, op_info, queue_index,
> > +                                       cb, opaque2);
> >  }
> >  
> >  static void
> > diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
> > index df4bde210b..39c8f5914e 100644
> > --- a/hw/virtio/virtio-crypto.c
> > +++ b/hw/virtio/virtio-crypto.c
> > @@ -27,6 +27,39 @@
> >  
> >  #define VIRTIO_CRYPTO_VM_VERSION 1
> >  
> > +typedef struct VirtIOCryptoSessionReq {
> > +    VirtIODevice *vdev;
> > +    VirtQueue *vq;
> > +    VirtQueueElement *elem;
> > +    CryptoDevBackendSessionInfo info;
> > +    CryptoDevCompletionFunc cb;
> > +} VirtIOCryptoSessionReq;
> > +
> > +static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq)
> > +{
> > +    switch (sreq->info.op_code) {
> > +    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> > +        g_free(sreq->info.u.sym_sess_info.cipher_key);
> > +        g_free(sreq->info.u.sym_sess_info.auth_key);
> > +        break;
> > +
> > +    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> > +        g_free(sreq->info.u.asym_sess_info.key);
> > +        break;
> > +
> > +    case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
> > +    case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
> > +    case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
> > +    case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
> > +    case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
> > +        break;
> > +
> > +    default:
> > +        error_report("Unknown opcode: %u", sreq->info.op_code);
> > +    }
> > +    g_free(sreq);
> > +}
> > +
> >  /*
> >   * Transfer virtqueue index to crypto queue index.
> >   * The control virtqueue is after the data virtqueues
> > @@ -75,27 +108,24 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
> >      return 0;
> >  }
> >  
> > -static int64_t
> > +static int
> >  virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
> >                 struct virtio_crypto_sym_create_session_req *sess_req,
> >                 uint32_t queue_id,
> >                 uint32_t opcode,
> > -               struct iovec *iov, unsigned int out_num)
> > +               struct iovec *iov, unsigned int out_num,
> > +               VirtIOCryptoSessionReq *sreq)
> >  {
> >      VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> > -    CryptoDevBackendSessionInfo info;
> > -    CryptoDevBackendSymSessionInfo *sym_info;
> > -    int64_t session_id;
> > +    CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info;
> >      int queue_index;
> >      uint32_t op_type;
> > -    Error *local_err = NULL;
> >      int ret;
> >  
> > -    memset(&info, 0, sizeof(info));
> >      op_type = ldl_le_p(&sess_req->op_type);
> > -    info.op_code = opcode;
> > +    sreq->info.op_code = opcode;
> >  
> > -    sym_info = &info.u.sym_sess_info;
> > +    sym_info = &sreq->info.u.sym_sess_info;
> >      sym_info->op_type = op_type;
> >  
> >      if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
> > @@ -103,7 +133,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
> >                             &sess_req->u.cipher.para,
> >                             &iov, &out_num);
> >          if (ret < 0) {
> > -            goto err;
> > +            return ret;
> >          }
> >      } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
> >          size_t s;
> > @@ -112,7 +142,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
> >                             &sess_req->u.chain.para.cipher_param,
> >                             &iov, &out_num);
> >          if (ret < 0) {
> > -            goto err;
> > +            return ret;
> >          }
> >          /* hash part */
> >          sym_info->alg_chain_order = ldl_le_p(
> > @@ -129,8 +159,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
> >              if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
> >                  error_report("virtio-crypto length of auth key is too big: %u",
> >                               sym_info->auth_key_len);
> > -                ret = -VIRTIO_CRYPTO_ERR;
> > -                goto err;
> > +                return -VIRTIO_CRYPTO_ERR;
> >              }
> >              /* get auth key */
> >              if (sym_info->auth_key_len > 0) {
> > @@ -140,8 +169,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
> >                  if (unlikely(s != sym_info->auth_key_len)) {
> >                      virtio_error(vdev,
> >                            "virtio-crypto authenticated key incorrect");
> > -                    ret = -EFAULT;
> > -                    goto err;
> > +                    return -EFAULT;
> >                  }
> >                  iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
> >              }
> > @@ -153,49 +181,30 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
> >          } else {
> >              /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
> >              error_report("unsupported hash mode");
> > -            ret = -VIRTIO_CRYPTO_NOTSUPP;
> > -            goto err;
> > +            return -VIRTIO_CRYPTO_NOTSUPP;
> >          }
> >      } else {
> >          /* VIRTIO_CRYPTO_SYM_OP_NONE */
> >          error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE");
> > -        ret = -VIRTIO_CRYPTO_NOTSUPP;
> > -        goto err;
> > +        return -VIRTIO_CRYPTO_NOTSUPP;
> >      }
> >  
> >      queue_index = virtio_crypto_vq2q(queue_id);
> > -    session_id = cryptodev_backend_create_session(
> > -                                     vcrypto->cryptodev,
> > -                                     &info, queue_index, &local_err);
> > -    if (session_id >= 0) {
> > -        ret = session_id;
> > -    } else {
> > -        if (local_err) {
> > -            error_report_err(local_err);
> > -        }
> > -        ret = -VIRTIO_CRYPTO_ERR;
> > -    }
> > -
> > -err:
> > -    g_free(sym_info->cipher_key);
> > -    g_free(sym_info->auth_key);
> > -    return ret;
> > +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
> > +                                            queue_index, sreq->cb, sreq);
> >  }
> >  
> > -static int64_t
> > +static int
> >  virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
> >                 struct virtio_crypto_akcipher_create_session_req *sess_req,
> >                 uint32_t queue_id, uint32_t opcode,
> > -               struct iovec *iov, unsigned int out_num)
> > +               struct iovec *iov, unsigned int out_num,
> > +               VirtIOCryptoSessionReq *sreq)
> >  {
> >      VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> > -    CryptoDevBackendSessionInfo info = {0};
> > -    CryptoDevBackendAsymSessionInfo *asym_info;
> > -    int64_t session_id;
> > +    CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info;
> >      int queue_index;
> >      uint32_t algo, keytype, keylen;
> > -    g_autofree uint8_t *key = NULL;
> > -    Error *local_err = NULL;
> >  
> >      algo = ldl_le_p(&sess_req->para.algo);
> >      keytype = ldl_le_p(&sess_req->para.keytype);
> > @@ -208,20 +217,19 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
> >      }
> >  
> >      if (keylen) {
> > -        key = g_malloc(keylen);
> > -        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
> > +        asym_info->key = g_malloc(keylen);
> > +        if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
> >              virtio_error(vdev, "virtio-crypto asym key incorrect");
> >              return -EFAULT;
> >          }
> >          iov_discard_front(&iov, &out_num, keylen);
> >      }
> >  
> > -    info.op_code = opcode;
> > -    asym_info = &info.u.asym_sess_info;
> > +    sreq->info.op_code = opcode;
> > +    asym_info = &sreq->info.u.asym_sess_info;
> >      asym_info->algo = algo;
> >      asym_info->keytype = keytype;
> >      asym_info->keylen = keylen;
> > -    asym_info->key = key;
> >      switch (asym_info->algo) {
> >      case VIRTIO_CRYPTO_AKCIPHER_RSA:
> >          asym_info->u.rsa.padding_algo =
> > @@ -237,45 +245,95 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
> >      }
> >  
> >      queue_index = virtio_crypto_vq2q(queue_id);
> > -    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
> > -                     queue_index, &local_err);
> > -    if (session_id < 0) {
> > -        if (local_err) {
> > -            error_report_err(local_err);
> > -        }
> > -        return -VIRTIO_CRYPTO_ERR;
> > -    }
> > -
> > -    return session_id;
> > +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
> > +                                            queue_index, sreq->cb, sreq);
> >  }
> >  
> > -static uint8_t
> > +static int
> >  virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
> >           struct virtio_crypto_destroy_session_req *close_sess_req,
> > -         uint32_t queue_id)
> > +         uint32_t queue_id,
> > +         VirtIOCryptoSessionReq *sreq)
> >  {
> > -    int ret;
> >      uint64_t session_id;
> > -    uint32_t status;
> > -    Error *local_err = NULL;
> >  
> >      session_id = ldq_le_p(&close_sess_req->session_id);
> >      DPRINTF("close session, id=%" PRIu64 "\n", session_id);
> >  
> > -    ret = cryptodev_backend_close_session(
> > -              vcrypto->cryptodev, session_id, queue_id, &local_err);
> > -    if (ret == 0) {
> > -        status = VIRTIO_CRYPTO_OK;
> > +    return cryptodev_backend_close_session(
> > +                vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq);
> > +}
> > +
> > +static void virtio_crypto_create_session_completion(void *opaque, int ret)
> > +{
> > +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
> > +    VirtQueue *vq = sreq->vq;
> > +    VirtQueueElement *elem = sreq->elem;
> > +    VirtIODevice *vdev = sreq->vdev;
> > +    struct virtio_crypto_session_input input;
> > +    struct iovec *in_iov = elem->in_sg;
> > +    unsigned in_num = elem->in_num;
> > +    size_t s;
> > +
> > +    memset(&input, 0, sizeof(input));
> > +    /* Serious errors, need to reset virtio crypto device */
> > +    if (ret == -EFAULT) {
> > +        virtqueue_detach_element(vq, elem, 0);
> > +        goto out;
> > +    } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
> > +        stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
> > +    } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) {
> > +        stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED);
> > +    } else if (ret != VIRTIO_CRYPTO_OK) {
> > +        stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
> >      } else {
> > -        if (local_err) {
> > -            error_report_err(local_err);
> > -        } else {
> > -            error_report("destroy session failed");
> > -        }
> > +        /* Set the session id */
> > +        stq_le_p(&input.session_id, sreq->info.session_id);
> > +        stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
> > +    }
> > +
> > +    s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
> > +    if (unlikely(s != sizeof(input))) {
> > +        virtio_error(vdev, "virtio-crypto input incorrect");
> > +        virtqueue_detach_element(vq, elem, 0);
> > +        goto out;
> > +    }
> > +    virtqueue_push(vq, elem, sizeof(input));
> > +    virtio_notify(vdev, vq);
> > +
> > +out:
> > +    g_free(elem);
> > +    virtio_crypto_free_create_session_req(sreq);
> > +}
> > +
> > +static void virtio_crypto_destroy_session_completion(void *opaque, int ret)
> > +{
> > +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
> > +    VirtQueue *vq = sreq->vq;
> > +    VirtQueueElement *elem = sreq->elem;
> > +    VirtIODevice *vdev = sreq->vdev;
> > +    struct iovec *in_iov = elem->in_sg;
> > +    unsigned in_num = elem->in_num;
> > +    uint8_t status;
> > +    size_t s;
> > +
> > +    if (ret < 0) {
> >          status = VIRTIO_CRYPTO_ERR;
> > +    } else {
> > +        status = VIRTIO_CRYPTO_OK;
> > +    }
> > +    s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
> > +    if (unlikely(s != sizeof(status))) {
> > +        virtio_error(vdev, "virtio-crypto status incorrect");
> > +        virtqueue_detach_element(vq, elem, 0);
> > +        goto out;
> >      }
> > +    virtqueue_push(vq, elem, sizeof(status));
> > +    virtio_notify(vdev, vq);
> >  
> > -    return status;
> > +out:
> > +    g_free(elem);
> > +    g_free(sreq);
> >  }
> >  
> >  static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
> > @@ -283,16 +341,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
> >      VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
> >      struct virtio_crypto_op_ctrl_req ctrl;
> >      VirtQueueElement *elem;
> > -    struct iovec *in_iov;
> > -    struct iovec *out_iov;
> > -    unsigned in_num;
> > +    VirtIOCryptoSessionReq *sreq;
> >      unsigned out_num;
> > +    unsigned in_num;
> >      uint32_t queue_id;
> >      uint32_t opcode;
> >      struct virtio_crypto_session_input input;
> > -    int64_t session_id;
> > -    uint8_t status;
> >      size_t s;
> > +    int ret;
> > +    struct iovec *out_iov;
> > +    struct iovec *in_iov;
> >  
> >      for (;;) {
> >          g_autofree struct iovec *out_iov_copy = NULL;
> > @@ -327,44 +385,34 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
> >          opcode = ldl_le_p(&ctrl.header.opcode);
> >          queue_id = ldl_le_p(&ctrl.header.queue_id);
> >  
> > -        memset(&input, 0, sizeof(input));
> > +        sreq = g_new0(VirtIOCryptoSessionReq, 1);
> > +        sreq->vdev = vdev;
> > +        sreq->vq = vq;
> > +        sreq->elem = elem;
> > +
> >          switch (opcode) {
> >          case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> > -            session_id = virtio_crypto_create_sym_session(vcrypto,
> > -                             &ctrl.u.sym_create_session,
> > -                             queue_id, opcode,
> > -                             out_iov, out_num);
> > -            goto check_session;
> > +            sreq->cb = virtio_crypto_create_session_completion;
> > +            ret = virtio_crypto_create_sym_session(vcrypto,
> > +                            &ctrl.u.sym_create_session,
> > +                            queue_id, opcode,
> > +                            out_iov, out_num,
> > +                            sreq);
> > +            if (ret < 0) {
> > +                virtio_crypto_create_session_completion(sreq, ret);
> > +            }
> > +            break;
> >  
> >          case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> > -            session_id = virtio_crypto_create_asym_session(vcrypto,
> > +            sreq->cb = virtio_crypto_create_session_completion;
> > +            ret = virtio_crypto_create_asym_session(vcrypto,
> >                               &ctrl.u.akcipher_create_session,
> >                               queue_id, opcode,
> > -                             out_iov, out_num);
> > -
> > -check_session:
> > -            /* Serious errors, need to reset virtio crypto device */
> > -            if (session_id == -EFAULT) {
> > -                virtqueue_detach_element(vq, elem, 0);
> > -                break;
> > -            } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) {
> > -                stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
> > -            } else if (session_id == -VIRTIO_CRYPTO_ERR) {
> > -                stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
> > -            } else {
> > -                /* Set the session id */
> > -                stq_le_p(&input.session_id, session_id);
> > -                stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
> > -            }
> > -
> > -            s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
> > -            if (unlikely(s != sizeof(input))) {
> > -                virtio_error(vdev, "virtio-crypto input incorrect");
> > -                virtqueue_detach_element(vq, elem, 0);
> > -                break;
> > +                             out_iov, out_num,
> > +                             sreq);
> > +            if (ret < 0) {
> > +                virtio_crypto_create_session_completion(sreq, ret);
> >              }
> > -            virtqueue_push(vq, elem, sizeof(input));
> > -            virtio_notify(vdev, vq);
> >              break;
> >  
> >          case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
> > @@ -372,37 +420,36 @@ check_session:
> >          case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
> >          case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
> >          case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
> > -            status = virtio_crypto_handle_close_session(vcrypto,
> > -                   &ctrl.u.destroy_session, queue_id);
> > -            /* The status only occupy one byte, we can directly use it */
> > -            s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
> > -            if (unlikely(s != sizeof(status))) {
> > -                virtio_error(vdev, "virtio-crypto status incorrect");
> > -                virtqueue_detach_element(vq, elem, 0);
> > -                break;
> > +            sreq->cb = virtio_crypto_destroy_session_completion;
> > +            ret = virtio_crypto_handle_close_session(vcrypto,
> > +                   &ctrl.u.destroy_session, queue_id,
> > +                   sreq);
> > +            if (ret < 0) {
> > +                virtio_crypto_destroy_session_completion(sreq, ret);
> >              }
> > -            virtqueue_push(vq, elem, sizeof(status));
> > -            virtio_notify(vdev, vq);
> >              break;
> > +
> >          case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
> >          case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
> >          case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
> >          default:
> > +            memset(&input, 0, sizeof(input));
> >              error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
> >              stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
> >              s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
> >              if (unlikely(s != sizeof(input))) {
> >                  virtio_error(vdev, "virtio-crypto input incorrect");
> >                  virtqueue_detach_element(vq, elem, 0);
> > -                break;
> > +            } else {
> > +                virtqueue_push(vq, elem, sizeof(input));
> > +                virtio_notify(vdev, vq);
> >              }
> > -            virtqueue_push(vq, elem, sizeof(input));
> > -            virtio_notify(vdev, vq);
> > +            g_free(sreq);
> > +            g_free(elem);
> >  
> >              break;
> >          } /* end switch case */
> >  
> > -        g_free(elem);
> >      } /* end for loop */
> >  }
> >  
> > @@ -513,11 +560,13 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
> >      req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
> >  }
> >  
> > -
> > -static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
> > +static void virtio_crypto_req_complete(void *opaque, int ret)
> >  {
> > +    VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque;
> >      VirtIOCrypto *vcrypto = req->vcrypto;
> >      VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> > +    uint8_t status = -ret;
> > +    g_autofree struct iovec *in_iov_copy = req->in_iov;
> >  
> >      if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> >          virtio_crypto_sym_input_data_helper(vdev, req, status,
> 
> I am not sure why is this g_autofree here.
> The variable is unused and clang gets confused (a bug in
> clang but oh well).
> Is the point to free when this goes out of scope?
> I suspect we should just g_free inside virtio_crypto_free_request
> or something like this.
> Pls send a fixup as otherwise I'll have to defer this until after
> the coming release.


Also to me it looks like not all paths call virtio_crypto_req_complete
so there might be a memory leak here.

Generally using g_autofree for something not allocated in the same
function is a hack.
I think I'll drop this patchset unless we can fix this quickly.


> 
> 
> > @@ -529,6 +578,7 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
> >      stb_p(&req->in->status, status);
> >      virtqueue_push(req->vq, &req->elem, req->in_len);
> >      virtio_notify(vdev, req->vq);
> > +    virtio_crypto_free_request(req);
> >  }
> >  
> >  static VirtIOCryptoReq *
> > @@ -773,9 +823,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
> >      unsigned in_num;
> >      unsigned out_num;
> >      uint32_t opcode;
> > -    uint8_t status = VIRTIO_CRYPTO_ERR;
> >      CryptoDevBackendOpInfo *op_info = &request->op_info;
> > -    Error *local_err = NULL;
> >  
> >      if (elem->out_num < 1 || elem->in_num < 1) {
> >          virtio_error(vdev, "virtio-crypto dataq missing headers");
> > @@ -815,6 +863,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
> >       */
> >      request->in_num = in_num;
> >      request->in_iov = in_iov;
> > +    /* now, we free the in_iov_copy inside virtio_crypto_req_complete */
> > +    in_iov_copy = NULL;
> >  
> >      opcode = ldl_le_p(&req.header.opcode);
> >      op_info->session_id = ldq_le_p(&req.header.session_id);
> > @@ -844,22 +894,11 @@ check_result:
> >              return -1;
> >          } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
> >              virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
> > -            virtio_crypto_free_request(request);
> >          } else {
> > -
> > -            /* Set request's parameter */
> > -            ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
> > -                                    request, queue_index, &local_err);
> > -            if (ret < 0) {
> > -                status = -ret;
> > -                if (local_err) {
> > -                    error_report_err(local_err);
> > -                }
> > -            } else { /* ret == VIRTIO_CRYPTO_OK */
> > -                status = ret;
> > -            }
> > -            virtio_crypto_req_complete(request, status);
> > -            virtio_crypto_free_request(request);
> > +            cryptodev_backend_crypto_operation(vcrypto->cryptodev,
> > +                                    request, queue_index,
> > +                                    virtio_crypto_req_complete,
> > +                                    request);
> >          }
> >          break;
> >  
> > @@ -871,7 +910,6 @@ check_result:
> >          error_report("virtio-crypto unsupported dataq opcode: %u",
> >                       opcode);
> >          virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
> > -        virtio_crypto_free_request(request);
> >      }
> >  
> >      return 0;
> > @@ -1011,7 +1049,7 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp)
> >          vcrypto->vqs[i].vcrypto = vcrypto;
> >      }
> >  
> > -    vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl);
> > +    vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl);
> >      if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) {
> >          vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY;
> >      } else {
> > diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
> > index 37c3a360fd..32e9f4cf8a 100644
> > --- a/include/sysemu/cryptodev.h
> > +++ b/include/sysemu/cryptodev.h
> > @@ -113,6 +113,7 @@ typedef struct CryptoDevBackendSessionInfo {
> >          CryptoDevBackendSymSessionInfo sym_sess_info;
> >          CryptoDevBackendAsymSessionInfo asym_sess_info;
> >      } u;
> > +    uint64_t session_id;
> >  } CryptoDevBackendSessionInfo;
> >  
> >  /**
> > @@ -188,21 +189,30 @@ typedef struct CryptoDevBackendOpInfo {
> >      } u;
> >  } CryptoDevBackendOpInfo;
> >  
> > +typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret);
> >  struct CryptoDevBackendClass {
> >      ObjectClass parent_class;
> >  
> >      void (*init)(CryptoDevBackend *backend, Error **errp);
> >      void (*cleanup)(CryptoDevBackend *backend, Error **errp);
> >  
> > -    int64_t (*create_session)(CryptoDevBackend *backend,
> > -                       CryptoDevBackendSessionInfo *sess_info,
> > -                       uint32_t queue_index, Error **errp);
> > +    int (*create_session)(CryptoDevBackend *backend,
> > +                          CryptoDevBackendSessionInfo *sess_info,
> > +                          uint32_t queue_index,
> > +                          CryptoDevCompletionFunc cb,
> > +                          void *opaque);
> > +
> >      int (*close_session)(CryptoDevBackend *backend,
> > -                           uint64_t session_id,
> > -                           uint32_t queue_index, Error **errp);
> > +                         uint64_t session_id,
> > +                         uint32_t queue_index,
> > +                         CryptoDevCompletionFunc cb,
> > +                         void *opaque);
> > +
> >      int (*do_op)(CryptoDevBackend *backend,
> > -                     CryptoDevBackendOpInfo *op_info,
> > -                     uint32_t queue_index, Error **errp);
> > +                 CryptoDevBackendOpInfo *op_info,
> > +                 uint32_t queue_index,
> > +                 CryptoDevCompletionFunc cb,
> > +                 void *opaque);
> >  };
> >  
> >  typedef enum CryptoDevBackendOptionsType {
> > @@ -303,15 +313,20 @@ void cryptodev_backend_cleanup(
> >   * @sess_info: parameters needed by session creating
> >   * @queue_index: queue index of cryptodev backend client
> >   * @errp: pointer to a NULL-initialized error object
> > + * @cb: callback when session create is compeleted
> > + * @opaque: parameter passed to callback
> >   *
> > - * Create a session for symmetric/symmetric algorithms
> > + * Create a session for symmetric/asymmetric algorithms
> >   *
> > - * Returns: session id on success, or -1 on error
> > + * Returns: 0 for success and cb will be called when creation is completed,
> > + * negative value for error, and cb will not be called.
> >   */
> > -int64_t cryptodev_backend_create_session(
> > +int cryptodev_backend_create_session(
> >             CryptoDevBackend *backend,
> >             CryptoDevBackendSessionInfo *sess_info,
> > -           uint32_t queue_index, Error **errp);
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque);
> >  
> >  /**
> >   * cryptodev_backend_close_session:
> > @@ -319,34 +334,43 @@ int64_t cryptodev_backend_create_session(
> >   * @session_id: the session id
> >   * @queue_index: queue index of cryptodev backend client
> >   * @errp: pointer to a NULL-initialized error object
> > + * @cb: callback when session create is compeleted
> > + * @opaque: parameter passed to callback
> >   *
> >   * Close a session for which was previously
> >   * created by cryptodev_backend_create_session()
> >   *
> > - * Returns: 0 on success, or Negative on error
> > + * Returns: 0 for success and cb will be called when creation is completed,
> > + * negative value for error, and cb will not be called.
> >   */
> >  int cryptodev_backend_close_session(
> >             CryptoDevBackend *backend,
> >             uint64_t session_id,
> > -           uint32_t queue_index, Error **errp);
> > +           uint32_t queue_index,
> > +           CryptoDevCompletionFunc cb,
> > +           void *opaque);
> >  
> >  /**
> >   * cryptodev_backend_crypto_operation:
> >   * @backend: the cryptodev backend object
> > - * @opaque: pointer to a VirtIOCryptoReq object
> > + * @opaque1: pointer to a VirtIOCryptoReq object
> >   * @queue_index: queue index of cryptodev backend client
> >   * @errp: pointer to a NULL-initialized error object
> > + * @cb: callbacks when operation is completed
> > + * @opaque2: parameter passed to cb
> >   *
> >   * Do crypto operation, such as encryption and
> >   * decryption
> >   *
> > - * Returns: VIRTIO_CRYPTO_OK on success,
> > - *         or -VIRTIO_CRYPTO_* on error
> > + * Returns: 0 for success and cb will be called when creation is completed,
> > + * negative value for error, and cb will not be called.
> >   */
> >  int cryptodev_backend_crypto_operation(
> >                   CryptoDevBackend *backend,
> > -                 void *opaque,
> > -                 uint32_t queue_index, Error **errp);
> > +                 void *opaque1,
> > +                 uint32_t queue_index,
> > +                 CryptoDevCompletionFunc cb,
> > +                 void *opaque2);
> >  
> >  /**
> >   * cryptodev_backend_set_used:
> > -- 
> > 2.11.0



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Re: [PATCH v2 1/4] virtio-crypto: Support asynchronous mode
  2022-11-01 19:51     ` Michael S. Tsirkin
@ 2022-11-02  2:24       ` Lei He
  2022-11-02  8:18       ` Lei He
  1 sibling, 0 replies; 12+ messages in thread
From: Lei He @ 2022-11-02  2:24 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: helei.sig11, berrange, arei.gonglei, pizhenwei, qemu-devel

On 2022/11/2 03:51, Michael S. Tsirkin wrote:
> On Tue, Nov 01, 2022 at 06:37:26AM -0400, Michael S. Tsirkin wrote:
>> On Sat, Oct 08, 2022 at 04:50:27PM +0800, Lei He wrote:
>>> virtio-crypto: Modify the current interface of virtio-crypto
>>> device to support asynchronous mode.
>>>
>>> Signed-off-by: lei he <helei.sig11@bytedance.com>
>>> ---
>>>   backends/cryptodev-builtin.c    |  69 ++++++---
>>>   backends/cryptodev-vhost-user.c |  51 +++++--
>>>   backends/cryptodev.c            |  44 +++---
>>>   hw/virtio/virtio-crypto.c       | 324 ++++++++++++++++++++++------------------
>>>   include/sysemu/cryptodev.h      |  60 +++++---
>>>   5 files changed, 336 insertions(+), 212 deletions(-)
>>>
>>> diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
>>> index 125cbad1d3..cda6ca3b71 100644
>>> --- a/backends/cryptodev-builtin.c
>>> +++ b/backends/cryptodev-builtin.c
>>> @@ -355,42 +355,62 @@ static int cryptodev_builtin_create_akcipher_session(
>>>       return index;
>>>   }
>>>   
>>> -static int64_t cryptodev_builtin_create_session(
>>> +static int cryptodev_builtin_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendBuiltin *builtin =
>>>                         CRYPTODEV_BACKEND_BUILTIN(backend);
>>>       CryptoDevBackendSymSessionInfo *sym_sess_info;
>>>       CryptoDevBackendAsymSessionInfo *asym_sess_info;
>>> +    int ret, status;
>>> +    Error *local_error = NULL;
>>>   
>>>       switch (sess_info->op_code) {
>>>       case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>>           sym_sess_info = &sess_info->u.sym_sess_info;
>>> -        return cryptodev_builtin_create_cipher_session(
>>> -                           builtin, sym_sess_info, errp);
>>> +        ret = cryptodev_builtin_create_cipher_session(
>>> +                    builtin, sym_sess_info, &local_error);
>>> +        break;
>>>   
>>>       case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>>>           asym_sess_info = &sess_info->u.asym_sess_info;
>>> -        return cryptodev_builtin_create_akcipher_session(
>>> -                           builtin, asym_sess_info, errp);
>>> +        ret = cryptodev_builtin_create_akcipher_session(
>>> +                           builtin, asym_sess_info, &local_error);
>>> +        break;
>>>   
>>>       case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>>>       case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>>>       default:
>>> -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
>>> +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
>>>                      sess_info->op_code);
>>> -        return -1;
>>> +        return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>> -    return -1;
>>> +    if (local_error) {
>>> +        error_report_err(local_error);
>>> +    }
>>> +    if (ret < 0) {
>>> +        status = -VIRTIO_CRYPTO_ERR;
>>> +    } else {
>>> +        sess_info->session_id = ret;
>>> +        status = VIRTIO_CRYPTO_OK;
>>> +    }
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static int cryptodev_builtin_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendBuiltin *builtin =
>>>                         CRYPTODEV_BACKEND_BUILTIN(backend);
>>> @@ -407,6 +427,9 @@ static int cryptodev_builtin_close_session(
>>>   
>>>       g_free(session);
>>>       builtin->sessions[session_id] = NULL;
>>> +    if (cb) {
>>> +        cb(opaque, VIRTIO_CRYPTO_OK);
>>> +    }
>>>       return 0;
>>>   }
>>>   
>>> @@ -506,7 +529,9 @@ static int cryptodev_builtin_asym_operation(
>>>   static int cryptodev_builtin_operation(
>>>                    CryptoDevBackend *backend,
>>>                    CryptoDevBackendOpInfo *op_info,
>>> -                 uint32_t queue_index, Error **errp)
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb,
>>> +                 void *opaque)
>>>   {
>>>       CryptoDevBackendBuiltin *builtin =
>>>                         CRYPTODEV_BACKEND_BUILTIN(backend);
>>> @@ -514,11 +539,12 @@ static int cryptodev_builtin_operation(
>>>       CryptoDevBackendSymOpInfo *sym_op_info;
>>>       CryptoDevBackendAsymOpInfo *asym_op_info;
>>>       enum CryptoDevBackendAlgType algtype = op_info->algtype;
>>> -    int ret = -VIRTIO_CRYPTO_ERR;
>>> +    int status = -VIRTIO_CRYPTO_ERR;
>>> +    Error *local_error = NULL;
>>>   
>>>       if (op_info->session_id >= MAX_NUM_SESSIONS ||
>>>                 builtin->sessions[op_info->session_id] == NULL) {
>>> -        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
>>> +        error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
>>>                      op_info->session_id);
>>>           return -VIRTIO_CRYPTO_INVSESS;
>>>       }
>>> @@ -526,14 +552,21 @@ static int cryptodev_builtin_operation(
>>>       sess = builtin->sessions[op_info->session_id];
>>>       if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
>>>           sym_op_info = op_info->u.sym_op_info;
>>> -        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
>>> +        status = cryptodev_builtin_sym_operation(sess, sym_op_info,
>>> +                                                 &local_error);
>>>       } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
>>>           asym_op_info = op_info->u.asym_op_info;
>>> -        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
>>> -                                               asym_op_info, errp);
>>> +        status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
>>> +                                                  asym_op_info, &local_error);
>>>       }
>>>   
>>> -    return ret;
>>> +    if (local_error) {
>>> +        error_report_err(local_error);
>>> +    }
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static void cryptodev_builtin_cleanup(
>>> @@ -548,7 +581,7 @@ static void cryptodev_builtin_cleanup(
>>>   
>>>       for (i = 0; i < MAX_NUM_SESSIONS; i++) {
>>>           if (builtin->sessions[i] != NULL) {
>>> -            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
>>> +            cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
>>>           }
>>>       }
>>>   
>>> diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
>>> index 5443a59153..351b2729e0 100644
>>> --- a/backends/cryptodev-vhost-user.c
>>> +++ b/backends/cryptodev-vhost-user.c
>>> @@ -259,13 +259,18 @@ static int64_t cryptodev_vhost_user_sym_create_session(
>>>       return -1;
>>>   }
>>>   
>>> -static int64_t cryptodev_vhost_user_create_session(
>>> +static int cryptodev_vhost_user_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       uint32_t op_code = sess_info->op_code;
>>>       CryptoDevBackendSymSessionInfo *sym_sess_info;
>>> +    int64_t ret;
>>> +    Error *local_error = NULL;
>>> +    int status;
>>>   
>>>       switch (op_code) {
>>>       case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>> @@ -273,27 +278,42 @@ static int64_t cryptodev_vhost_user_create_session(
>>>       case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>>>       case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>>>           sym_sess_info = &sess_info->u.sym_sess_info;
>>> -        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
>>> -                   queue_index, errp);
>>> +        ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
>>> +                   queue_index, &local_error);
>>> +        break;
>>> +
>>>       default:
>>> -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
>>> +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
>>>                      sess_info->op_code);
>>> -        return -1;
>>> -
>>> +        return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>> -    return -1;
>>> +    if (local_error) {
>>> +        error_report_err(local_error);
>>> +    }
>>> +    if (ret < 0) {
>>> +        status = -VIRTIO_CRYPTO_ERR;
>>> +    } else {
>>> +        sess_info->session_id = ret;
>>> +        status = VIRTIO_CRYPTO_OK;
>>> +    }
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static int cryptodev_vhost_user_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendClient *cc =
>>>                     backend->conf.peers.ccs[queue_index];
>>>       CryptoDevBackendVhost *vhost_crypto;
>>> -    int ret;
>>> +    int ret = -1, status;
>>>   
>>>       vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
>>>       if (vhost_crypto) {
>>> @@ -301,12 +321,17 @@ static int cryptodev_vhost_user_close_session(
>>>           ret = dev->vhost_ops->vhost_crypto_close_session(dev,
>>>                                                            session_id);
>>>           if (ret < 0) {
>>> -            return -1;
>>> +            status = -VIRTIO_CRYPTO_ERR;
>>>           } else {
>>> -            return 0;
>>> +            status = VIRTIO_CRYPTO_OK;
>>>           }
>>> +    } else {
>>> +        status = -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>> -    return -1;
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static void cryptodev_vhost_user_cleanup(
>>> diff --git a/backends/cryptodev.c b/backends/cryptodev.c
>>> index 33eb4e1a70..54ee8c81f5 100644
>>> --- a/backends/cryptodev.c
>>> +++ b/backends/cryptodev.c
>>> @@ -26,6 +26,7 @@
>>>   #include "qapi/error.h"
>>>   #include "qapi/visitor.h"
>>>   #include "qemu/config-file.h"
>>> +#include "qemu/error-report.h"
>>>   #include "qom/object_interfaces.h"
>>>   #include "hw/virtio/virtio-crypto.h"
>>>   
>>> @@ -72,69 +73,72 @@ void cryptodev_backend_cleanup(
>>>       }
>>>   }
>>>   
>>> -int64_t cryptodev_backend_create_session(
>>> +int cryptodev_backend_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendClass *bc =
>>>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>>>   
>>>       if (bc->create_session) {
>>> -        return bc->create_session(backend, sess_info, queue_index, errp);
>>> +        return bc->create_session(backend, sess_info, queue_index, cb, opaque);
>>>       }
>>> -
>>> -    return -1;
>>> +    return -VIRTIO_CRYPTO_NOTSUPP;
>>>   }
>>>   
>>>   int cryptodev_backend_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendClass *bc =
>>>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>>>   
>>>       if (bc->close_session) {
>>> -        return bc->close_session(backend, session_id, queue_index, errp);
>>> +        return bc->close_session(backend, session_id, queue_index, cb, opaque);
>>>       }
>>> -
>>> -    return -1;
>>> +    return -VIRTIO_CRYPTO_NOTSUPP;
>>>   }
>>>   
>>>   static int cryptodev_backend_operation(
>>>                    CryptoDevBackend *backend,
>>>                    CryptoDevBackendOpInfo *op_info,
>>> -                 uint32_t queue_index, Error **errp)
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb,
>>> +                 void *opaque)
>>>   {
>>>       CryptoDevBackendClass *bc =
>>>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>>>   
>>>       if (bc->do_op) {
>>> -        return bc->do_op(backend, op_info, queue_index, errp);
>>> +        return bc->do_op(backend, op_info, queue_index, cb, opaque);
>>>       }
>>> -
>>> -    return -VIRTIO_CRYPTO_ERR;
>>> +    return -VIRTIO_CRYPTO_NOTSUPP;
>>>   }
>>>   
>>>   int cryptodev_backend_crypto_operation(
>>>                    CryptoDevBackend *backend,
>>> -                 void *opaque,
>>> -                 uint32_t queue_index, Error **errp)
>>> +                 void *opaque1,
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb, void *opaque2)
>>>   {
>>> -    VirtIOCryptoReq *req = opaque;
>>> +    VirtIOCryptoReq *req = opaque1;
>>>       CryptoDevBackendOpInfo *op_info = &req->op_info;
>>>       enum CryptoDevBackendAlgType algtype = req->flags;
>>>   
>>>       if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
>>>           && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
>>> -        error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
>>> -                   algtype);
>>> -
>>> +        error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype);
>>>           return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>> -    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
>>> +    return cryptodev_backend_operation(backend, op_info, queue_index,
>>> +                                       cb, opaque2);
>>>   }
>>>   
>>>   static void
>>> diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
>>> index df4bde210b..39c8f5914e 100644
>>> --- a/hw/virtio/virtio-crypto.c
>>> +++ b/hw/virtio/virtio-crypto.c
>>> @@ -27,6 +27,39 @@
>>>   
>>>   #define VIRTIO_CRYPTO_VM_VERSION 1
>>>   
>>> +typedef struct VirtIOCryptoSessionReq {
>>> +    VirtIODevice *vdev;
>>> +    VirtQueue *vq;
>>> +    VirtQueueElement *elem;
>>> +    CryptoDevBackendSessionInfo info;
>>> +    CryptoDevCompletionFunc cb;
>>> +} VirtIOCryptoSessionReq;
>>> +
>>> +static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq)
>>> +{
>>> +    switch (sreq->info.op_code) {
>>> +    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>> +        g_free(sreq->info.u.sym_sess_info.cipher_key);
>>> +        g_free(sreq->info.u.sym_sess_info.auth_key);
>>> +        break;
>>> +
>>> +    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>>> +        g_free(sreq->info.u.asym_sess_info.key);
>>> +        break;
>>> +
>>> +    case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
>>> +        break;
>>> +
>>> +    default:
>>> +        error_report("Unknown opcode: %u", sreq->info.op_code);
>>> +    }
>>> +    g_free(sreq);
>>> +}
>>> +
>>>   /*
>>>    * Transfer virtqueue index to crypto queue index.
>>>    * The control virtqueue is after the data virtqueues
>>> @@ -75,27 +108,24 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
>>>       return 0;
>>>   }
>>>   
>>> -static int64_t
>>> +static int
>>>   virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                  struct virtio_crypto_sym_create_session_req *sess_req,
>>>                  uint32_t queue_id,
>>>                  uint32_t opcode,
>>> -               struct iovec *iov, unsigned int out_num)
>>> +               struct iovec *iov, unsigned int out_num,
>>> +               VirtIOCryptoSessionReq *sreq)
>>>   {
>>>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
>>> -    CryptoDevBackendSessionInfo info;
>>> -    CryptoDevBackendSymSessionInfo *sym_info;
>>> -    int64_t session_id;
>>> +    CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info;
>>>       int queue_index;
>>>       uint32_t op_type;
>>> -    Error *local_err = NULL;
>>>       int ret;
>>>   
>>> -    memset(&info, 0, sizeof(info));
>>>       op_type = ldl_le_p(&sess_req->op_type);
>>> -    info.op_code = opcode;
>>> +    sreq->info.op_code = opcode;
>>>   
>>> -    sym_info = &info.u.sym_sess_info;
>>> +    sym_info = &sreq->info.u.sym_sess_info;
>>>       sym_info->op_type = op_type;
>>>   
>>>       if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
>>> @@ -103,7 +133,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                              &sess_req->u.cipher.para,
>>>                              &iov, &out_num);
>>>           if (ret < 0) {
>>> -            goto err;
>>> +            return ret;
>>>           }
>>>       } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
>>>           size_t s;
>>> @@ -112,7 +142,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                              &sess_req->u.chain.para.cipher_param,
>>>                              &iov, &out_num);
>>>           if (ret < 0) {
>>> -            goto err;
>>> +            return ret;
>>>           }
>>>           /* hash part */
>>>           sym_info->alg_chain_order = ldl_le_p(
>>> @@ -129,8 +159,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>               if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
>>>                   error_report("virtio-crypto length of auth key is too big: %u",
>>>                                sym_info->auth_key_len);
>>> -                ret = -VIRTIO_CRYPTO_ERR;
>>> -                goto err;
>>> +                return -VIRTIO_CRYPTO_ERR;
>>>               }
>>>               /* get auth key */
>>>               if (sym_info->auth_key_len > 0) {
>>> @@ -140,8 +169,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                   if (unlikely(s != sym_info->auth_key_len)) {
>>>                       virtio_error(vdev,
>>>                             "virtio-crypto authenticated key incorrect");
>>> -                    ret = -EFAULT;
>>> -                    goto err;
>>> +                    return -EFAULT;
>>>                   }
>>>                   iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
>>>               }
>>> @@ -153,49 +181,30 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>           } else {
>>>               /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
>>>               error_report("unsupported hash mode");
>>> -            ret = -VIRTIO_CRYPTO_NOTSUPP;
>>> -            goto err;
>>> +            return -VIRTIO_CRYPTO_NOTSUPP;
>>>           }
>>>       } else {
>>>           /* VIRTIO_CRYPTO_SYM_OP_NONE */
>>>           error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE");
>>> -        ret = -VIRTIO_CRYPTO_NOTSUPP;
>>> -        goto err;
>>> +        return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>>       queue_index = virtio_crypto_vq2q(queue_id);
>>> -    session_id = cryptodev_backend_create_session(
>>> -                                     vcrypto->cryptodev,
>>> -                                     &info, queue_index, &local_err);
>>> -    if (session_id >= 0) {
>>> -        ret = session_id;
>>> -    } else {
>>> -        if (local_err) {
>>> -            error_report_err(local_err);
>>> -        }
>>> -        ret = -VIRTIO_CRYPTO_ERR;
>>> -    }
>>> -
>>> -err:
>>> -    g_free(sym_info->cipher_key);
>>> -    g_free(sym_info->auth_key);
>>> -    return ret;
>>> +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
>>> +                                            queue_index, sreq->cb, sreq);
>>>   }
>>>   
>>> -static int64_t
>>> +static int
>>>   virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>>>                  struct virtio_crypto_akcipher_create_session_req *sess_req,
>>>                  uint32_t queue_id, uint32_t opcode,
>>> -               struct iovec *iov, unsigned int out_num)
>>> +               struct iovec *iov, unsigned int out_num,
>>> +               VirtIOCryptoSessionReq *sreq)
>>>   {
>>>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
>>> -    CryptoDevBackendSessionInfo info = {0};
>>> -    CryptoDevBackendAsymSessionInfo *asym_info;
>>> -    int64_t session_id;
>>> +    CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info;
>>>       int queue_index;
>>>       uint32_t algo, keytype, keylen;
>>> -    g_autofree uint8_t *key = NULL;
>>> -    Error *local_err = NULL;
>>>   
>>>       algo = ldl_le_p(&sess_req->para.algo);
>>>       keytype = ldl_le_p(&sess_req->para.keytype);
>>> @@ -208,20 +217,19 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>>>       }
>>>   
>>>       if (keylen) {
>>> -        key = g_malloc(keylen);
>>> -        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
>>> +        asym_info->key = g_malloc(keylen);
>>> +        if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
>>>               virtio_error(vdev, "virtio-crypto asym key incorrect");
>>>               return -EFAULT;
>>>           }
>>>           iov_discard_front(&iov, &out_num, keylen);
>>>       }
>>>   
>>> -    info.op_code = opcode;
>>> -    asym_info = &info.u.asym_sess_info;
>>> +    sreq->info.op_code = opcode;
>>> +    asym_info = &sreq->info.u.asym_sess_info;
>>>       asym_info->algo = algo;
>>>       asym_info->keytype = keytype;
>>>       asym_info->keylen = keylen;
>>> -    asym_info->key = key;
>>>       switch (asym_info->algo) {
>>>       case VIRTIO_CRYPTO_AKCIPHER_RSA:
>>>           asym_info->u.rsa.padding_algo =
>>> @@ -237,45 +245,95 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>>>       }
>>>   
>>>       queue_index = virtio_crypto_vq2q(queue_id);
>>> -    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
>>> -                     queue_index, &local_err);
>>> -    if (session_id < 0) {
>>> -        if (local_err) {
>>> -            error_report_err(local_err);
>>> -        }
>>> -        return -VIRTIO_CRYPTO_ERR;
>>> -    }
>>> -
>>> -    return session_id;
>>> +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
>>> +                                            queue_index, sreq->cb, sreq);
>>>   }
>>>   
>>> -static uint8_t
>>> +static int
>>>   virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
>>>            struct virtio_crypto_destroy_session_req *close_sess_req,
>>> -         uint32_t queue_id)
>>> +         uint32_t queue_id,
>>> +         VirtIOCryptoSessionReq *sreq)
>>>   {
>>> -    int ret;
>>>       uint64_t session_id;
>>> -    uint32_t status;
>>> -    Error *local_err = NULL;
>>>   
>>>       session_id = ldq_le_p(&close_sess_req->session_id);
>>>       DPRINTF("close session, id=%" PRIu64 "\n", session_id);
>>>   
>>> -    ret = cryptodev_backend_close_session(
>>> -              vcrypto->cryptodev, session_id, queue_id, &local_err);
>>> -    if (ret == 0) {
>>> -        status = VIRTIO_CRYPTO_OK;
>>> +    return cryptodev_backend_close_session(
>>> +                vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq);
>>> +}
>>> +
>>> +static void virtio_crypto_create_session_completion(void *opaque, int ret)
>>> +{
>>> +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
>>> +    VirtQueue *vq = sreq->vq;
>>> +    VirtQueueElement *elem = sreq->elem;
>>> +    VirtIODevice *vdev = sreq->vdev;
>>> +    struct virtio_crypto_session_input input;
>>> +    struct iovec *in_iov = elem->in_sg;
>>> +    unsigned in_num = elem->in_num;
>>> +    size_t s;
>>> +
>>> +    memset(&input, 0, sizeof(input));
>>> +    /* Serious errors, need to reset virtio crypto device */
>>> +    if (ret == -EFAULT) {
>>> +        virtqueue_detach_element(vq, elem, 0);
>>> +        goto out;
>>> +    } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>>> +    } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) {
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED);
>>> +    } else if (ret != VIRTIO_CRYPTO_OK) {
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
>>>       } else {
>>> -        if (local_err) {
>>> -            error_report_err(local_err);
>>> -        } else {
>>> -            error_report("destroy session failed");
>>> -        }
>>> +        /* Set the session id */
>>> +        stq_le_p(&input.session_id, sreq->info.session_id);
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
>>> +    }
>>> +
>>> +    s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>>> +    if (unlikely(s != sizeof(input))) {
>>> +        virtio_error(vdev, "virtio-crypto input incorrect");
>>> +        virtqueue_detach_element(vq, elem, 0);
>>> +        goto out;
>>> +    }
>>> +    virtqueue_push(vq, elem, sizeof(input));
>>> +    virtio_notify(vdev, vq);
>>> +
>>> +out:
>>> +    g_free(elem);
>>> +    virtio_crypto_free_create_session_req(sreq);
>>> +}
>>> +
>>> +static void virtio_crypto_destroy_session_completion(void *opaque, int ret)
>>> +{
>>> +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
>>> +    VirtQueue *vq = sreq->vq;
>>> +    VirtQueueElement *elem = sreq->elem;
>>> +    VirtIODevice *vdev = sreq->vdev;
>>> +    struct iovec *in_iov = elem->in_sg;
>>> +    unsigned in_num = elem->in_num;
>>> +    uint8_t status;
>>> +    size_t s;
>>> +
>>> +    if (ret < 0) {
>>>           status = VIRTIO_CRYPTO_ERR;
>>> +    } else {
>>> +        status = VIRTIO_CRYPTO_OK;
>>> +    }
>>> +    s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
>>> +    if (unlikely(s != sizeof(status))) {
>>> +        virtio_error(vdev, "virtio-crypto status incorrect");
>>> +        virtqueue_detach_element(vq, elem, 0);
>>> +        goto out;
>>>       }
>>> +    virtqueue_push(vq, elem, sizeof(status));
>>> +    virtio_notify(vdev, vq);
>>>   
>>> -    return status;
>>> +out:
>>> +    g_free(elem);
>>> +    g_free(sreq);
>>>   }
>>>   
>>>   static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>>> @@ -283,16 +341,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>>>       VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
>>>       struct virtio_crypto_op_ctrl_req ctrl;
>>>       VirtQueueElement *elem;
>>> -    struct iovec *in_iov;
>>> -    struct iovec *out_iov;
>>> -    unsigned in_num;
>>> +    VirtIOCryptoSessionReq *sreq;
>>>       unsigned out_num;
>>> +    unsigned in_num;
>>>       uint32_t queue_id;
>>>       uint32_t opcode;
>>>       struct virtio_crypto_session_input input;
>>> -    int64_t session_id;
>>> -    uint8_t status;
>>>       size_t s;
>>> +    int ret;
>>> +    struct iovec *out_iov;
>>> +    struct iovec *in_iov;
>>>   
>>>       for (;;) {
>>>           g_autofree struct iovec *out_iov_copy = NULL;
>>> @@ -327,44 +385,34 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>>>           opcode = ldl_le_p(&ctrl.header.opcode);
>>>           queue_id = ldl_le_p(&ctrl.header.queue_id);
>>>   
>>> -        memset(&input, 0, sizeof(input));
>>> +        sreq = g_new0(VirtIOCryptoSessionReq, 1);
>>> +        sreq->vdev = vdev;
>>> +        sreq->vq = vq;
>>> +        sreq->elem = elem;
>>> +
>>>           switch (opcode) {
>>>           case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>> -            session_id = virtio_crypto_create_sym_session(vcrypto,
>>> -                             &ctrl.u.sym_create_session,
>>> -                             queue_id, opcode,
>>> -                             out_iov, out_num);
>>> -            goto check_session;
>>> +            sreq->cb = virtio_crypto_create_session_completion;
>>> +            ret = virtio_crypto_create_sym_session(vcrypto,
>>> +                            &ctrl.u.sym_create_session,
>>> +                            queue_id, opcode,
>>> +                            out_iov, out_num,
>>> +                            sreq);
>>> +            if (ret < 0) {
>>> +                virtio_crypto_create_session_completion(sreq, ret);
>>> +            }
>>> +            break;
>>>   
>>>           case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>>> -            session_id = virtio_crypto_create_asym_session(vcrypto,
>>> +            sreq->cb = virtio_crypto_create_session_completion;
>>> +            ret = virtio_crypto_create_asym_session(vcrypto,
>>>                                &ctrl.u.akcipher_create_session,
>>>                                queue_id, opcode,
>>> -                             out_iov, out_num);
>>> -
>>> -check_session:
>>> -            /* Serious errors, need to reset virtio crypto device */
>>> -            if (session_id == -EFAULT) {
>>> -                virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> -            } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) {
>>> -                stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>>> -            } else if (session_id == -VIRTIO_CRYPTO_ERR) {
>>> -                stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
>>> -            } else {
>>> -                /* Set the session id */
>>> -                stq_le_p(&input.session_id, session_id);
>>> -                stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
>>> -            }
>>> -
>>> -            s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>>> -            if (unlikely(s != sizeof(input))) {
>>> -                virtio_error(vdev, "virtio-crypto input incorrect");
>>> -                virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> +                             out_iov, out_num,
>>> +                             sreq);
>>> +            if (ret < 0) {
>>> +                virtio_crypto_create_session_completion(sreq, ret);
>>>               }
>>> -            virtqueue_push(vq, elem, sizeof(input));
>>> -            virtio_notify(vdev, vq);
>>>               break;
>>>   
>>>           case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
>>> @@ -372,37 +420,36 @@ check_session:
>>>           case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
>>>           case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
>>>           case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
>>> -            status = virtio_crypto_handle_close_session(vcrypto,
>>> -                   &ctrl.u.destroy_session, queue_id);
>>> -            /* The status only occupy one byte, we can directly use it */
>>> -            s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
>>> -            if (unlikely(s != sizeof(status))) {
>>> -                virtio_error(vdev, "virtio-crypto status incorrect");
>>> -                virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> +            sreq->cb = virtio_crypto_destroy_session_completion;
>>> +            ret = virtio_crypto_handle_close_session(vcrypto,
>>> +                   &ctrl.u.destroy_session, queue_id,
>>> +                   sreq);
>>> +            if (ret < 0) {
>>> +                virtio_crypto_destroy_session_completion(sreq, ret);
>>>               }
>>> -            virtqueue_push(vq, elem, sizeof(status));
>>> -            virtio_notify(vdev, vq);
>>>               break;
>>> +
>>>           case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>>>           case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>>>           case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>>>           default:
>>> +            memset(&input, 0, sizeof(input));
>>>               error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
>>>               stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>>>               s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>>>               if (unlikely(s != sizeof(input))) {
>>>                   virtio_error(vdev, "virtio-crypto input incorrect");
>>>                   virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> +            } else {
>>> +                virtqueue_push(vq, elem, sizeof(input));
>>> +                virtio_notify(vdev, vq);
>>>               }
>>> -            virtqueue_push(vq, elem, sizeof(input));
>>> -            virtio_notify(vdev, vq);
>>> +            g_free(sreq);
>>> +            g_free(elem);
>>>   
>>>               break;
>>>           } /* end switch case */
>>>   
>>> -        g_free(elem);
>>>       } /* end for loop */
>>>   }
>>>   
>>> @@ -513,11 +560,13 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
>>>       req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
>>>   }
>>>   
>>> -
>>> -static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>>> +static void virtio_crypto_req_complete(void *opaque, int ret)
>>>   {
>>> +    VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque;
>>>       VirtIOCrypto *vcrypto = req->vcrypto;
>>>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
>>> +    uint8_t status = -ret;
>>> +    g_autofree struct iovec *in_iov_copy = req->in_iov;
>>>   
>>>       if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
>>>           virtio_crypto_sym_input_data_helper(vdev, req, status,
>>
>> I am not sure why is this g_autofree here.
>> The variable is unused and clang gets confused (a bug in
>> clang but oh well).
>> Is the point to free when this goes out of scope?
>> I suspect we should just g_free inside virtio_crypto_free_request
>> or something like this.
>> Pls send a fixup as otherwise I'll have to defer this until after
>> the coming release.
> 
> 
> Also to me it looks like not all paths call virtio_crypto_req_complete
> so there might be a memory leak here.
> 
> Generally using g_autofree for something not allocated in the same
> function is a hack.
> I think I'll drop this patchset unless we can fix this quickly.
> 

  Thanks for reminding, I will prioritize fixing this today.


Best regards,
Lei He
--
helei.sig11@bytedance.com



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Re: [PATCH v2 1/4] virtio-crypto: Support asynchronous mode
  2022-11-01 19:51     ` Michael S. Tsirkin
  2022-11-02  2:24       ` Lei He
@ 2022-11-02  8:18       ` Lei He
  1 sibling, 0 replies; 12+ messages in thread
From: Lei He @ 2022-11-02  8:18 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: helei.sig11, berrange, arei.gonglei, pizhenwei, qemu-devel

On 2022/11/2 03:51, Michael S. Tsirkin wrote:
> On Tue, Nov 01, 2022 at 06:37:26AM -0400, Michael S. Tsirkin wrote:
>> On Sat, Oct 08, 2022 at 04:50:27PM +0800, Lei He wrote:
>>> virtio-crypto: Modify the current interface of virtio-crypto
>>> device to support asynchronous mode.
>>>
>>> Signed-off-by: lei he <helei.sig11@bytedance.com>
>>> ---
>>>   backends/cryptodev-builtin.c    |  69 ++++++---
>>>   backends/cryptodev-vhost-user.c |  51 +++++--
>>>   backends/cryptodev.c            |  44 +++---
>>>   hw/virtio/virtio-crypto.c       | 324 ++++++++++++++++++++++------------------
>>>   include/sysemu/cryptodev.h      |  60 +++++---
>>>   5 files changed, 336 insertions(+), 212 deletions(-)
>>>
>>> diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
>>> index 125cbad1d3..cda6ca3b71 100644
>>> --- a/backends/cryptodev-builtin.c
>>> +++ b/backends/cryptodev-builtin.c
>>> @@ -355,42 +355,62 @@ static int cryptodev_builtin_create_akcipher_session(
>>>       return index;
>>>   }
>>>   
>>> -static int64_t cryptodev_builtin_create_session(
>>> +static int cryptodev_builtin_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendBuiltin *builtin =
>>>                         CRYPTODEV_BACKEND_BUILTIN(backend);
>>>       CryptoDevBackendSymSessionInfo *sym_sess_info;
>>>       CryptoDevBackendAsymSessionInfo *asym_sess_info;
>>> +    int ret, status;
>>> +    Error *local_error = NULL;
>>>   
>>>       switch (sess_info->op_code) {
>>>       case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>>           sym_sess_info = &sess_info->u.sym_sess_info;
>>> -        return cryptodev_builtin_create_cipher_session(
>>> -                           builtin, sym_sess_info, errp);
>>> +        ret = cryptodev_builtin_create_cipher_session(
>>> +                    builtin, sym_sess_info, &local_error);
>>> +        break;
>>>   
>>>       case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>>>           asym_sess_info = &sess_info->u.asym_sess_info;
>>> -        return cryptodev_builtin_create_akcipher_session(
>>> -                           builtin, asym_sess_info, errp);
>>> +        ret = cryptodev_builtin_create_akcipher_session(
>>> +                           builtin, asym_sess_info, &local_error);
>>> +        break;
>>>   
>>>       case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>>>       case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>>>       default:
>>> -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
>>> +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
>>>                      sess_info->op_code);
>>> -        return -1;
>>> +        return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>> -    return -1;
>>> +    if (local_error) {
>>> +        error_report_err(local_error);
>>> +    }
>>> +    if (ret < 0) {
>>> +        status = -VIRTIO_CRYPTO_ERR;
>>> +    } else {
>>> +        sess_info->session_id = ret;
>>> +        status = VIRTIO_CRYPTO_OK;
>>> +    }
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static int cryptodev_builtin_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendBuiltin *builtin =
>>>                         CRYPTODEV_BACKEND_BUILTIN(backend);
>>> @@ -407,6 +427,9 @@ static int cryptodev_builtin_close_session(
>>>   
>>>       g_free(session);
>>>       builtin->sessions[session_id] = NULL;
>>> +    if (cb) {
>>> +        cb(opaque, VIRTIO_CRYPTO_OK);
>>> +    }
>>>       return 0;
>>>   }
>>>   
>>> @@ -506,7 +529,9 @@ static int cryptodev_builtin_asym_operation(
>>>   static int cryptodev_builtin_operation(
>>>                    CryptoDevBackend *backend,
>>>                    CryptoDevBackendOpInfo *op_info,
>>> -                 uint32_t queue_index, Error **errp)
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb,
>>> +                 void *opaque)
>>>   {
>>>       CryptoDevBackendBuiltin *builtin =
>>>                         CRYPTODEV_BACKEND_BUILTIN(backend);
>>> @@ -514,11 +539,12 @@ static int cryptodev_builtin_operation(
>>>       CryptoDevBackendSymOpInfo *sym_op_info;
>>>       CryptoDevBackendAsymOpInfo *asym_op_info;
>>>       enum CryptoDevBackendAlgType algtype = op_info->algtype;
>>> -    int ret = -VIRTIO_CRYPTO_ERR;
>>> +    int status = -VIRTIO_CRYPTO_ERR;
>>> +    Error *local_error = NULL;
>>>   
>>>       if (op_info->session_id >= MAX_NUM_SESSIONS ||
>>>                 builtin->sessions[op_info->session_id] == NULL) {
>>> -        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
>>> +        error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
>>>                      op_info->session_id);
>>>           return -VIRTIO_CRYPTO_INVSESS;
>>>       }
>>> @@ -526,14 +552,21 @@ static int cryptodev_builtin_operation(
>>>       sess = builtin->sessions[op_info->session_id];
>>>       if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
>>>           sym_op_info = op_info->u.sym_op_info;
>>> -        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
>>> +        status = cryptodev_builtin_sym_operation(sess, sym_op_info,
>>> +                                                 &local_error);
>>>       } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
>>>           asym_op_info = op_info->u.asym_op_info;
>>> -        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
>>> -                                               asym_op_info, errp);
>>> +        status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
>>> +                                                  asym_op_info, &local_error);
>>>       }
>>>   
>>> -    return ret;
>>> +    if (local_error) {
>>> +        error_report_err(local_error);
>>> +    }
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static void cryptodev_builtin_cleanup(
>>> @@ -548,7 +581,7 @@ static void cryptodev_builtin_cleanup(
>>>   
>>>       for (i = 0; i < MAX_NUM_SESSIONS; i++) {
>>>           if (builtin->sessions[i] != NULL) {
>>> -            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
>>> +            cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
>>>           }
>>>       }
>>>   
>>> diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
>>> index 5443a59153..351b2729e0 100644
>>> --- a/backends/cryptodev-vhost-user.c
>>> +++ b/backends/cryptodev-vhost-user.c
>>> @@ -259,13 +259,18 @@ static int64_t cryptodev_vhost_user_sym_create_session(
>>>       return -1;
>>>   }
>>>   
>>> -static int64_t cryptodev_vhost_user_create_session(
>>> +static int cryptodev_vhost_user_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       uint32_t op_code = sess_info->op_code;
>>>       CryptoDevBackendSymSessionInfo *sym_sess_info;
>>> +    int64_t ret;
>>> +    Error *local_error = NULL;
>>> +    int status;
>>>   
>>>       switch (op_code) {
>>>       case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>> @@ -273,27 +278,42 @@ static int64_t cryptodev_vhost_user_create_session(
>>>       case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>>>       case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>>>           sym_sess_info = &sess_info->u.sym_sess_info;
>>> -        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
>>> -                   queue_index, errp);
>>> +        ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
>>> +                   queue_index, &local_error);
>>> +        break;
>>> +
>>>       default:
>>> -        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
>>> +        error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
>>>                      sess_info->op_code);
>>> -        return -1;
>>> -
>>> +        return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>> -    return -1;
>>> +    if (local_error) {
>>> +        error_report_err(local_error);
>>> +    }
>>> +    if (ret < 0) {
>>> +        status = -VIRTIO_CRYPTO_ERR;
>>> +    } else {
>>> +        sess_info->session_id = ret;
>>> +        status = VIRTIO_CRYPTO_OK;
>>> +    }
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static int cryptodev_vhost_user_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendClient *cc =
>>>                     backend->conf.peers.ccs[queue_index];
>>>       CryptoDevBackendVhost *vhost_crypto;
>>> -    int ret;
>>> +    int ret = -1, status;
>>>   
>>>       vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
>>>       if (vhost_crypto) {
>>> @@ -301,12 +321,17 @@ static int cryptodev_vhost_user_close_session(
>>>           ret = dev->vhost_ops->vhost_crypto_close_session(dev,
>>>                                                            session_id);
>>>           if (ret < 0) {
>>> -            return -1;
>>> +            status = -VIRTIO_CRYPTO_ERR;
>>>           } else {
>>> -            return 0;
>>> +            status = VIRTIO_CRYPTO_OK;
>>>           }
>>> +    } else {
>>> +        status = -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>> -    return -1;
>>> +    if (cb) {
>>> +        cb(opaque, status);
>>> +    }
>>> +    return 0;
>>>   }
>>>   
>>>   static void cryptodev_vhost_user_cleanup(
>>> diff --git a/backends/cryptodev.c b/backends/cryptodev.c
>>> index 33eb4e1a70..54ee8c81f5 100644
>>> --- a/backends/cryptodev.c
>>> +++ b/backends/cryptodev.c
>>> @@ -26,6 +26,7 @@
>>>   #include "qapi/error.h"
>>>   #include "qapi/visitor.h"
>>>   #include "qemu/config-file.h"
>>> +#include "qemu/error-report.h"
>>>   #include "qom/object_interfaces.h"
>>>   #include "hw/virtio/virtio-crypto.h"
>>>   
>>> @@ -72,69 +73,72 @@ void cryptodev_backend_cleanup(
>>>       }
>>>   }
>>>   
>>> -int64_t cryptodev_backend_create_session(
>>> +int cryptodev_backend_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendClass *bc =
>>>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>>>   
>>>       if (bc->create_session) {
>>> -        return bc->create_session(backend, sess_info, queue_index, errp);
>>> +        return bc->create_session(backend, sess_info, queue_index, cb, opaque);
>>>       }
>>> -
>>> -    return -1;
>>> +    return -VIRTIO_CRYPTO_NOTSUPP;
>>>   }
>>>   
>>>   int cryptodev_backend_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp)
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque)
>>>   {
>>>       CryptoDevBackendClass *bc =
>>>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>>>   
>>>       if (bc->close_session) {
>>> -        return bc->close_session(backend, session_id, queue_index, errp);
>>> +        return bc->close_session(backend, session_id, queue_index, cb, opaque);
>>>       }
>>> -
>>> -    return -1;
>>> +    return -VIRTIO_CRYPTO_NOTSUPP;
>>>   }
>>>   
>>>   static int cryptodev_backend_operation(
>>>                    CryptoDevBackend *backend,
>>>                    CryptoDevBackendOpInfo *op_info,
>>> -                 uint32_t queue_index, Error **errp)
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb,
>>> +                 void *opaque)
>>>   {
>>>       CryptoDevBackendClass *bc =
>>>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>>>   
>>>       if (bc->do_op) {
>>> -        return bc->do_op(backend, op_info, queue_index, errp);
>>> +        return bc->do_op(backend, op_info, queue_index, cb, opaque);
>>>       }
>>> -
>>> -    return -VIRTIO_CRYPTO_ERR;
>>> +    return -VIRTIO_CRYPTO_NOTSUPP;
>>>   }
>>>   
>>>   int cryptodev_backend_crypto_operation(
>>>                    CryptoDevBackend *backend,
>>> -                 void *opaque,
>>> -                 uint32_t queue_index, Error **errp)
>>> +                 void *opaque1,
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb, void *opaque2)
>>>   {
>>> -    VirtIOCryptoReq *req = opaque;
>>> +    VirtIOCryptoReq *req = opaque1;
>>>       CryptoDevBackendOpInfo *op_info = &req->op_info;
>>>       enum CryptoDevBackendAlgType algtype = req->flags;
>>>   
>>>       if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
>>>           && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
>>> -        error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
>>> -                   algtype);
>>> -
>>> +        error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype);
>>>           return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>> -    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
>>> +    return cryptodev_backend_operation(backend, op_info, queue_index,
>>> +                                       cb, opaque2);
>>>   }
>>>   
>>>   static void
>>> diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
>>> index df4bde210b..39c8f5914e 100644
>>> --- a/hw/virtio/virtio-crypto.c
>>> +++ b/hw/virtio/virtio-crypto.c
>>> @@ -27,6 +27,39 @@
>>>   
>>>   #define VIRTIO_CRYPTO_VM_VERSION 1
>>>   
>>> +typedef struct VirtIOCryptoSessionReq {
>>> +    VirtIODevice *vdev;
>>> +    VirtQueue *vq;
>>> +    VirtQueueElement *elem;
>>> +    CryptoDevBackendSessionInfo info;
>>> +    CryptoDevCompletionFunc cb;
>>> +} VirtIOCryptoSessionReq;
>>> +
>>> +static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq)
>>> +{
>>> +    switch (sreq->info.op_code) {
>>> +    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>> +        g_free(sreq->info.u.sym_sess_info.cipher_key);
>>> +        g_free(sreq->info.u.sym_sess_info.auth_key);
>>> +        break;
>>> +
>>> +    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>>> +        g_free(sreq->info.u.asym_sess_info.key);
>>> +        break;
>>> +
>>> +    case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
>>> +    case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
>>> +        break;
>>> +
>>> +    default:
>>> +        error_report("Unknown opcode: %u", sreq->info.op_code);
>>> +    }
>>> +    g_free(sreq);
>>> +}
>>> +
>>>   /*
>>>    * Transfer virtqueue index to crypto queue index.
>>>    * The control virtqueue is after the data virtqueues
>>> @@ -75,27 +108,24 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
>>>       return 0;
>>>   }
>>>   
>>> -static int64_t
>>> +static int
>>>   virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                  struct virtio_crypto_sym_create_session_req *sess_req,
>>>                  uint32_t queue_id,
>>>                  uint32_t opcode,
>>> -               struct iovec *iov, unsigned int out_num)
>>> +               struct iovec *iov, unsigned int out_num,
>>> +               VirtIOCryptoSessionReq *sreq)
>>>   {
>>>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
>>> -    CryptoDevBackendSessionInfo info;
>>> -    CryptoDevBackendSymSessionInfo *sym_info;
>>> -    int64_t session_id;
>>> +    CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info;
>>>       int queue_index;
>>>       uint32_t op_type;
>>> -    Error *local_err = NULL;
>>>       int ret;
>>>   
>>> -    memset(&info, 0, sizeof(info));
>>>       op_type = ldl_le_p(&sess_req->op_type);
>>> -    info.op_code = opcode;
>>> +    sreq->info.op_code = opcode;
>>>   
>>> -    sym_info = &info.u.sym_sess_info;
>>> +    sym_info = &sreq->info.u.sym_sess_info;
>>>       sym_info->op_type = op_type;
>>>   
>>>       if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
>>> @@ -103,7 +133,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                              &sess_req->u.cipher.para,
>>>                              &iov, &out_num);
>>>           if (ret < 0) {
>>> -            goto err;
>>> +            return ret;
>>>           }
>>>       } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
>>>           size_t s;
>>> @@ -112,7 +142,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                              &sess_req->u.chain.para.cipher_param,
>>>                              &iov, &out_num);
>>>           if (ret < 0) {
>>> -            goto err;
>>> +            return ret;
>>>           }
>>>           /* hash part */
>>>           sym_info->alg_chain_order = ldl_le_p(
>>> @@ -129,8 +159,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>               if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
>>>                   error_report("virtio-crypto length of auth key is too big: %u",
>>>                                sym_info->auth_key_len);
>>> -                ret = -VIRTIO_CRYPTO_ERR;
>>> -                goto err;
>>> +                return -VIRTIO_CRYPTO_ERR;
>>>               }
>>>               /* get auth key */
>>>               if (sym_info->auth_key_len > 0) {
>>> @@ -140,8 +169,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>                   if (unlikely(s != sym_info->auth_key_len)) {
>>>                       virtio_error(vdev,
>>>                             "virtio-crypto authenticated key incorrect");
>>> -                    ret = -EFAULT;
>>> -                    goto err;
>>> +                    return -EFAULT;
>>>                   }
>>>                   iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
>>>               }
>>> @@ -153,49 +181,30 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>>>           } else {
>>>               /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
>>>               error_report("unsupported hash mode");
>>> -            ret = -VIRTIO_CRYPTO_NOTSUPP;
>>> -            goto err;
>>> +            return -VIRTIO_CRYPTO_NOTSUPP;
>>>           }
>>>       } else {
>>>           /* VIRTIO_CRYPTO_SYM_OP_NONE */
>>>           error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE");
>>> -        ret = -VIRTIO_CRYPTO_NOTSUPP;
>>> -        goto err;
>>> +        return -VIRTIO_CRYPTO_NOTSUPP;
>>>       }
>>>   
>>>       queue_index = virtio_crypto_vq2q(queue_id);
>>> -    session_id = cryptodev_backend_create_session(
>>> -                                     vcrypto->cryptodev,
>>> -                                     &info, queue_index, &local_err);
>>> -    if (session_id >= 0) {
>>> -        ret = session_id;
>>> -    } else {
>>> -        if (local_err) {
>>> -            error_report_err(local_err);
>>> -        }
>>> -        ret = -VIRTIO_CRYPTO_ERR;
>>> -    }
>>> -
>>> -err:
>>> -    g_free(sym_info->cipher_key);
>>> -    g_free(sym_info->auth_key);
>>> -    return ret;
>>> +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
>>> +                                            queue_index, sreq->cb, sreq);
>>>   }
>>>   
>>> -static int64_t
>>> +static int
>>>   virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>>>                  struct virtio_crypto_akcipher_create_session_req *sess_req,
>>>                  uint32_t queue_id, uint32_t opcode,
>>> -               struct iovec *iov, unsigned int out_num)
>>> +               struct iovec *iov, unsigned int out_num,
>>> +               VirtIOCryptoSessionReq *sreq)
>>>   {
>>>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
>>> -    CryptoDevBackendSessionInfo info = {0};
>>> -    CryptoDevBackendAsymSessionInfo *asym_info;
>>> -    int64_t session_id;
>>> +    CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info;
>>>       int queue_index;
>>>       uint32_t algo, keytype, keylen;
>>> -    g_autofree uint8_t *key = NULL;
>>> -    Error *local_err = NULL;
>>>   
>>>       algo = ldl_le_p(&sess_req->para.algo);
>>>       keytype = ldl_le_p(&sess_req->para.keytype);
>>> @@ -208,20 +217,19 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>>>       }
>>>   
>>>       if (keylen) {
>>> -        key = g_malloc(keylen);
>>> -        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
>>> +        asym_info->key = g_malloc(keylen);
>>> +        if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
>>>               virtio_error(vdev, "virtio-crypto asym key incorrect");
>>>               return -EFAULT;
>>>           }
>>>           iov_discard_front(&iov, &out_num, keylen);
>>>       }
>>>   
>>> -    info.op_code = opcode;
>>> -    asym_info = &info.u.asym_sess_info;
>>> +    sreq->info.op_code = opcode;
>>> +    asym_info = &sreq->info.u.asym_sess_info;
>>>       asym_info->algo = algo;
>>>       asym_info->keytype = keytype;
>>>       asym_info->keylen = keylen;
>>> -    asym_info->key = key;
>>>       switch (asym_info->algo) {
>>>       case VIRTIO_CRYPTO_AKCIPHER_RSA:
>>>           asym_info->u.rsa.padding_algo =
>>> @@ -237,45 +245,95 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
>>>       }
>>>   
>>>       queue_index = virtio_crypto_vq2q(queue_id);
>>> -    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
>>> -                     queue_index, &local_err);
>>> -    if (session_id < 0) {
>>> -        if (local_err) {
>>> -            error_report_err(local_err);
>>> -        }
>>> -        return -VIRTIO_CRYPTO_ERR;
>>> -    }
>>> -
>>> -    return session_id;
>>> +    return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
>>> +                                            queue_index, sreq->cb, sreq);
>>>   }
>>>   
>>> -static uint8_t
>>> +static int
>>>   virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
>>>            struct virtio_crypto_destroy_session_req *close_sess_req,
>>> -         uint32_t queue_id)
>>> +         uint32_t queue_id,
>>> +         VirtIOCryptoSessionReq *sreq)
>>>   {
>>> -    int ret;
>>>       uint64_t session_id;
>>> -    uint32_t status;
>>> -    Error *local_err = NULL;
>>>   
>>>       session_id = ldq_le_p(&close_sess_req->session_id);
>>>       DPRINTF("close session, id=%" PRIu64 "\n", session_id);
>>>   
>>> -    ret = cryptodev_backend_close_session(
>>> -              vcrypto->cryptodev, session_id, queue_id, &local_err);
>>> -    if (ret == 0) {
>>> -        status = VIRTIO_CRYPTO_OK;
>>> +    return cryptodev_backend_close_session(
>>> +                vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq);
>>> +}
>>> +
>>> +static void virtio_crypto_create_session_completion(void *opaque, int ret)
>>> +{
>>> +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
>>> +    VirtQueue *vq = sreq->vq;
>>> +    VirtQueueElement *elem = sreq->elem;
>>> +    VirtIODevice *vdev = sreq->vdev;
>>> +    struct virtio_crypto_session_input input;
>>> +    struct iovec *in_iov = elem->in_sg;
>>> +    unsigned in_num = elem->in_num;
>>> +    size_t s;
>>> +
>>> +    memset(&input, 0, sizeof(input));
>>> +    /* Serious errors, need to reset virtio crypto device */
>>> +    if (ret == -EFAULT) {
>>> +        virtqueue_detach_element(vq, elem, 0);
>>> +        goto out;
>>> +    } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>>> +    } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) {
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED);
>>> +    } else if (ret != VIRTIO_CRYPTO_OK) {
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
>>>       } else {
>>> -        if (local_err) {
>>> -            error_report_err(local_err);
>>> -        } else {
>>> -            error_report("destroy session failed");
>>> -        }
>>> +        /* Set the session id */
>>> +        stq_le_p(&input.session_id, sreq->info.session_id);
>>> +        stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
>>> +    }
>>> +
>>> +    s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>>> +    if (unlikely(s != sizeof(input))) {
>>> +        virtio_error(vdev, "virtio-crypto input incorrect");
>>> +        virtqueue_detach_element(vq, elem, 0);
>>> +        goto out;
>>> +    }
>>> +    virtqueue_push(vq, elem, sizeof(input));
>>> +    virtio_notify(vdev, vq);
>>> +
>>> +out:
>>> +    g_free(elem);
>>> +    virtio_crypto_free_create_session_req(sreq);
>>> +}
>>> +
>>> +static void virtio_crypto_destroy_session_completion(void *opaque, int ret)
>>> +{
>>> +    VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
>>> +    VirtQueue *vq = sreq->vq;
>>> +    VirtQueueElement *elem = sreq->elem;
>>> +    VirtIODevice *vdev = sreq->vdev;
>>> +    struct iovec *in_iov = elem->in_sg;
>>> +    unsigned in_num = elem->in_num;
>>> +    uint8_t status;
>>> +    size_t s;
>>> +
>>> +    if (ret < 0) {
>>>           status = VIRTIO_CRYPTO_ERR;
>>> +    } else {
>>> +        status = VIRTIO_CRYPTO_OK;
>>> +    }
>>> +    s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
>>> +    if (unlikely(s != sizeof(status))) {
>>> +        virtio_error(vdev, "virtio-crypto status incorrect");
>>> +        virtqueue_detach_element(vq, elem, 0);
>>> +        goto out;
>>>       }
>>> +    virtqueue_push(vq, elem, sizeof(status));
>>> +    virtio_notify(vdev, vq);
>>>   
>>> -    return status;
>>> +out:
>>> +    g_free(elem);
>>> +    g_free(sreq);
>>>   }
>>>   
>>>   static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>>> @@ -283,16 +341,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>>>       VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
>>>       struct virtio_crypto_op_ctrl_req ctrl;
>>>       VirtQueueElement *elem;
>>> -    struct iovec *in_iov;
>>> -    struct iovec *out_iov;
>>> -    unsigned in_num;
>>> +    VirtIOCryptoSessionReq *sreq;
>>>       unsigned out_num;
>>> +    unsigned in_num;
>>>       uint32_t queue_id;
>>>       uint32_t opcode;
>>>       struct virtio_crypto_session_input input;
>>> -    int64_t session_id;
>>> -    uint8_t status;
>>>       size_t s;
>>> +    int ret;
>>> +    struct iovec *out_iov;
>>> +    struct iovec *in_iov;
>>>   
>>>       for (;;) {
>>>           g_autofree struct iovec *out_iov_copy = NULL;
>>> @@ -327,44 +385,34 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>>>           opcode = ldl_le_p(&ctrl.header.opcode);
>>>           queue_id = ldl_le_p(&ctrl.header.queue_id);
>>>   
>>> -        memset(&input, 0, sizeof(input));
>>> +        sreq = g_new0(VirtIOCryptoSessionReq, 1);
>>> +        sreq->vdev = vdev;
>>> +        sreq->vq = vq;
>>> +        sreq->elem = elem;
>>> +
>>>           switch (opcode) {
>>>           case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
>>> -            session_id = virtio_crypto_create_sym_session(vcrypto,
>>> -                             &ctrl.u.sym_create_session,
>>> -                             queue_id, opcode,
>>> -                             out_iov, out_num);
>>> -            goto check_session;
>>> +            sreq->cb = virtio_crypto_create_session_completion;
>>> +            ret = virtio_crypto_create_sym_session(vcrypto,
>>> +                            &ctrl.u.sym_create_session,
>>> +                            queue_id, opcode,
>>> +                            out_iov, out_num,
>>> +                            sreq);
>>> +            if (ret < 0) {
>>> +                virtio_crypto_create_session_completion(sreq, ret);
>>> +            }
>>> +            break;
>>>   
>>>           case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
>>> -            session_id = virtio_crypto_create_asym_session(vcrypto,
>>> +            sreq->cb = virtio_crypto_create_session_completion;
>>> +            ret = virtio_crypto_create_asym_session(vcrypto,
>>>                                &ctrl.u.akcipher_create_session,
>>>                                queue_id, opcode,
>>> -                             out_iov, out_num);
>>> -
>>> -check_session:
>>> -            /* Serious errors, need to reset virtio crypto device */
>>> -            if (session_id == -EFAULT) {
>>> -                virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> -            } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) {
>>> -                stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>>> -            } else if (session_id == -VIRTIO_CRYPTO_ERR) {
>>> -                stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
>>> -            } else {
>>> -                /* Set the session id */
>>> -                stq_le_p(&input.session_id, session_id);
>>> -                stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
>>> -            }
>>> -
>>> -            s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>>> -            if (unlikely(s != sizeof(input))) {
>>> -                virtio_error(vdev, "virtio-crypto input incorrect");
>>> -                virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> +                             out_iov, out_num,
>>> +                             sreq);
>>> +            if (ret < 0) {
>>> +                virtio_crypto_create_session_completion(sreq, ret);
>>>               }
>>> -            virtqueue_push(vq, elem, sizeof(input));
>>> -            virtio_notify(vdev, vq);
>>>               break;
>>>   
>>>           case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
>>> @@ -372,37 +420,36 @@ check_session:
>>>           case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
>>>           case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
>>>           case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
>>> -            status = virtio_crypto_handle_close_session(vcrypto,
>>> -                   &ctrl.u.destroy_session, queue_id);
>>> -            /* The status only occupy one byte, we can directly use it */
>>> -            s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
>>> -            if (unlikely(s != sizeof(status))) {
>>> -                virtio_error(vdev, "virtio-crypto status incorrect");
>>> -                virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> +            sreq->cb = virtio_crypto_destroy_session_completion;
>>> +            ret = virtio_crypto_handle_close_session(vcrypto,
>>> +                   &ctrl.u.destroy_session, queue_id,
>>> +                   sreq);
>>> +            if (ret < 0) {
>>> +                virtio_crypto_destroy_session_completion(sreq, ret);
>>>               }
>>> -            virtqueue_push(vq, elem, sizeof(status));
>>> -            virtio_notify(vdev, vq);
>>>               break;
>>> +
>>>           case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>>>           case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>>>           case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>>>           default:
>>> +            memset(&input, 0, sizeof(input));
>>>               error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
>>>               stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>>>               s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>>>               if (unlikely(s != sizeof(input))) {
>>>                   virtio_error(vdev, "virtio-crypto input incorrect");
>>>                   virtqueue_detach_element(vq, elem, 0);
>>> -                break;
>>> +            } else {
>>> +                virtqueue_push(vq, elem, sizeof(input));
>>> +                virtio_notify(vdev, vq);
>>>               }
>>> -            virtqueue_push(vq, elem, sizeof(input));
>>> -            virtio_notify(vdev, vq);
>>> +            g_free(sreq);
>>> +            g_free(elem);
>>>   
>>>               break;
>>>           } /* end switch case */
>>>   
>>> -        g_free(elem);
>>>       } /* end for loop */
>>>   }
>>>   
>>> @@ -513,11 +560,13 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
>>>       req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
>>>   }
>>>   
>>> -
>>> -static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>>> +static void virtio_crypto_req_complete(void *opaque, int ret)
>>>   {
>>> +    VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque;
>>>       VirtIOCrypto *vcrypto = req->vcrypto;
>>>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
>>> +    uint8_t status = -ret;
>>> +    g_autofree struct iovec *in_iov_copy = req->in_iov;
>>>   
>>>       if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
>>>           virtio_crypto_sym_input_data_helper(vdev, req, status,
>>
>> I am not sure why is this g_autofree here.
>> The variable is unused and clang gets confused (a bug in
>> clang but oh well).
>> Is the point to free when this goes out of scope?
>> I suspect we should just g_free inside virtio_crypto_free_request
>> or something like this.

virtio_crypto_sym_input_data_helper may call
iov_discard_front(&req->in_iov...), so I saved req->in_iov and try to
free it with g_autofree. Anyway, g_free has now been used instead
of g_autofree.

>> Pls send a fixup as otherwise I'll have to defer this until after
>> the coming release.
> 
> 
> Also to me it looks like not all paths call virtio_crypto_req_complete
> so there might be a memory leak here.

Fixed.

> 
> Generally using g_autofree for something not allocated in the same
> function is a hack.
> I think I'll drop this patchset unless we can fix this quickly.
> 

V3 is sent and thanks a lot for the review. If there are any other
issues, I will respond as soon as possible.

> 
>>
>>
>>> @@ -529,6 +578,7 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>>>       stb_p(&req->in->status, status);
>>>       virtqueue_push(req->vq, &req->elem, req->in_len);
>>>       virtio_notify(vdev, req->vq);
>>> +    virtio_crypto_free_request(req);
>>>   }
>>>   
>>>   static VirtIOCryptoReq *
>>> @@ -773,9 +823,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>>>       unsigned in_num;
>>>       unsigned out_num;
>>>       uint32_t opcode;
>>> -    uint8_t status = VIRTIO_CRYPTO_ERR;
>>>       CryptoDevBackendOpInfo *op_info = &request->op_info;
>>> -    Error *local_err = NULL;
>>>   
>>>       if (elem->out_num < 1 || elem->in_num < 1) {
>>>           virtio_error(vdev, "virtio-crypto dataq missing headers");
>>> @@ -815,6 +863,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>>>        */
>>>       request->in_num = in_num;
>>>       request->in_iov = in_iov;
>>> +    /* now, we free the in_iov_copy inside virtio_crypto_req_complete */
>>> +    in_iov_copy = NULL;
>>>   
>>>       opcode = ldl_le_p(&req.header.opcode);
>>>       op_info->session_id = ldq_le_p(&req.header.session_id);
>>> @@ -844,22 +894,11 @@ check_result:
>>>               return -1;
>>>           } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
>>>               virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
>>> -            virtio_crypto_free_request(request);
>>>           } else {
>>> -
>>> -            /* Set request's parameter */
>>> -            ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
>>> -                                    request, queue_index, &local_err);
>>> -            if (ret < 0) {
>>> -                status = -ret;
>>> -                if (local_err) {
>>> -                    error_report_err(local_err);
>>> -                }
>>> -            } else { /* ret == VIRTIO_CRYPTO_OK */
>>> -                status = ret;
>>> -            }
>>> -            virtio_crypto_req_complete(request, status);
>>> -            virtio_crypto_free_request(request);
>>> +            cryptodev_backend_crypto_operation(vcrypto->cryptodev,
>>> +                                    request, queue_index,
>>> +                                    virtio_crypto_req_complete,
>>> +                                    request);
>>>           }
>>>           break;
>>>   
>>> @@ -871,7 +910,6 @@ check_result:
>>>           error_report("virtio-crypto unsupported dataq opcode: %u",
>>>                        opcode);
>>>           virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
>>> -        virtio_crypto_free_request(request);
>>>       }
>>>   
>>>       return 0;
>>> @@ -1011,7 +1049,7 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp)
>>>           vcrypto->vqs[i].vcrypto = vcrypto;
>>>       }
>>>   
>>> -    vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl);
>>> +    vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl);
>>>       if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) {
>>>           vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY;
>>>       } else {
>>> diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
>>> index 37c3a360fd..32e9f4cf8a 100644
>>> --- a/include/sysemu/cryptodev.h
>>> +++ b/include/sysemu/cryptodev.h
>>> @@ -113,6 +113,7 @@ typedef struct CryptoDevBackendSessionInfo {
>>>           CryptoDevBackendSymSessionInfo sym_sess_info;
>>>           CryptoDevBackendAsymSessionInfo asym_sess_info;
>>>       } u;
>>> +    uint64_t session_id;
>>>   } CryptoDevBackendSessionInfo;
>>>   
>>>   /**
>>> @@ -188,21 +189,30 @@ typedef struct CryptoDevBackendOpInfo {
>>>       } u;
>>>   } CryptoDevBackendOpInfo;
>>>   
>>> +typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret);
>>>   struct CryptoDevBackendClass {
>>>       ObjectClass parent_class;
>>>   
>>>       void (*init)(CryptoDevBackend *backend, Error **errp);
>>>       void (*cleanup)(CryptoDevBackend *backend, Error **errp);
>>>   
>>> -    int64_t (*create_session)(CryptoDevBackend *backend,
>>> -                       CryptoDevBackendSessionInfo *sess_info,
>>> -                       uint32_t queue_index, Error **errp);
>>> +    int (*create_session)(CryptoDevBackend *backend,
>>> +                          CryptoDevBackendSessionInfo *sess_info,
>>> +                          uint32_t queue_index,
>>> +                          CryptoDevCompletionFunc cb,
>>> +                          void *opaque);
>>> +
>>>       int (*close_session)(CryptoDevBackend *backend,
>>> -                           uint64_t session_id,
>>> -                           uint32_t queue_index, Error **errp);
>>> +                         uint64_t session_id,
>>> +                         uint32_t queue_index,
>>> +                         CryptoDevCompletionFunc cb,
>>> +                         void *opaque);
>>> +
>>>       int (*do_op)(CryptoDevBackend *backend,
>>> -                     CryptoDevBackendOpInfo *op_info,
>>> -                     uint32_t queue_index, Error **errp);
>>> +                 CryptoDevBackendOpInfo *op_info,
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb,
>>> +                 void *opaque);
>>>   };
>>>   
>>>   typedef enum CryptoDevBackendOptionsType {
>>> @@ -303,15 +313,20 @@ void cryptodev_backend_cleanup(
>>>    * @sess_info: parameters needed by session creating
>>>    * @queue_index: queue index of cryptodev backend client
>>>    * @errp: pointer to a NULL-initialized error object
>>> + * @cb: callback when session create is compeleted
>>> + * @opaque: parameter passed to callback
>>>    *
>>> - * Create a session for symmetric/symmetric algorithms
>>> + * Create a session for symmetric/asymmetric algorithms
>>>    *
>>> - * Returns: session id on success, or -1 on error
>>> + * Returns: 0 for success and cb will be called when creation is completed,
>>> + * negative value for error, and cb will not be called.
>>>    */
>>> -int64_t cryptodev_backend_create_session(
>>> +int cryptodev_backend_create_session(
>>>              CryptoDevBackend *backend,
>>>              CryptoDevBackendSessionInfo *sess_info,
>>> -           uint32_t queue_index, Error **errp);
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque);
>>>   
>>>   /**
>>>    * cryptodev_backend_close_session:
>>> @@ -319,34 +334,43 @@ int64_t cryptodev_backend_create_session(
>>>    * @session_id: the session id
>>>    * @queue_index: queue index of cryptodev backend client
>>>    * @errp: pointer to a NULL-initialized error object
>>> + * @cb: callback when session create is compeleted
>>> + * @opaque: parameter passed to callback
>>>    *
>>>    * Close a session for which was previously
>>>    * created by cryptodev_backend_create_session()
>>>    *
>>> - * Returns: 0 on success, or Negative on error
>>> + * Returns: 0 for success and cb will be called when creation is completed,
>>> + * negative value for error, and cb will not be called.
>>>    */
>>>   int cryptodev_backend_close_session(
>>>              CryptoDevBackend *backend,
>>>              uint64_t session_id,
>>> -           uint32_t queue_index, Error **errp);
>>> +           uint32_t queue_index,
>>> +           CryptoDevCompletionFunc cb,
>>> +           void *opaque);
>>>   
>>>   /**
>>>    * cryptodev_backend_crypto_operation:
>>>    * @backend: the cryptodev backend object
>>> - * @opaque: pointer to a VirtIOCryptoReq object
>>> + * @opaque1: pointer to a VirtIOCryptoReq object
>>>    * @queue_index: queue index of cryptodev backend client
>>>    * @errp: pointer to a NULL-initialized error object
>>> + * @cb: callbacks when operation is completed
>>> + * @opaque2: parameter passed to cb
>>>    *
>>>    * Do crypto operation, such as encryption and
>>>    * decryption
>>>    *
>>> - * Returns: VIRTIO_CRYPTO_OK on success,
>>> - *         or -VIRTIO_CRYPTO_* on error
>>> + * Returns: 0 for success and cb will be called when creation is completed,
>>> + * negative value for error, and cb will not be called.
>>>    */
>>>   int cryptodev_backend_crypto_operation(
>>>                    CryptoDevBackend *backend,
>>> -                 void *opaque,
>>> -                 uint32_t queue_index, Error **errp);
>>> +                 void *opaque1,
>>> +                 uint32_t queue_index,
>>> +                 CryptoDevCompletionFunc cb,
>>> +                 void *opaque2);
>>>   
>>>   /**
>>>    * cryptodev_backend_set_used:
>>> -- 
>>> 2.11.0
> 

Best regards,
Lei He
--
helei.sig11@bytedance.com



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2022-11-02  8:19 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-08  8:50 [PATCH v2 0/4] Add a new backend for cryptodev Lei He
2022-10-08  8:50 ` [PATCH v2 1/4] virtio-crypto: Support asynchronous mode Lei He
2022-11-01 10:37   ` Michael S. Tsirkin
2022-11-01 19:51     ` Michael S. Tsirkin
2022-11-02  2:24       ` Lei He
2022-11-02  8:18       ` Lei He
2022-10-08  8:50 ` [PATCH v2 2/4] crypto: Support DER encodings Lei He
2022-10-11  9:31   ` Daniel P. Berrangé
2022-10-08  8:50 ` [PATCH v2 3/4] crypto: Support export akcipher to pkcs8 Lei He
2022-10-11  9:33   ` Daniel P. Berrangé
2022-10-08  8:50 ` [PATCH v2 4/4] cryptodev: Add a lkcf-backend for cryptodev Lei He
2022-10-24  8:55 ` [PATCH v2 0/4] Add a new backend " Lei He

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).