static void virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, VirtIOCryptoReq *req, uint32_t status, CryptoDevBackendSymOpInfo *sym_op_info) { size_t s, len; if (status != VIRTIO_CRYPTO_OK) { return; } len = sym_op_info->src_len; /* Save the cipher result */ s = iov_from_buf(req->in_iov, req->in_num, 0, sym_op_info->dst, len); if (s != len) { virtio_error(vdev, "virtio-crypto dest data incorrect"); return; } iov_discard_front(&req->in_iov, &req->in_num, len); if (sym_op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { /* Save the digest result */ s = iov_from_buf(req->in_iov, req->in_num, 0, sym_op_info->digest_result, sym_op_info->digest_result_len); if (s != sym_op_info->digest_result_len) { virtio_error(vdev, "virtio-crypto digest result incorrect"); } } }
ssize_t nbd_wr_syncv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length, bool do_read) { ssize_t done = 0; Error *local_err = NULL; struct iovec *local_iov = g_new(struct iovec, niov); struct iovec *local_iov_head = local_iov; unsigned int nlocal_iov = niov; nlocal_iov = iov_copy(local_iov, nlocal_iov, iov, niov, 0, length); while (nlocal_iov > 0) { ssize_t len; if (do_read) { len = qio_channel_readv(ioc, local_iov, nlocal_iov, &local_err); } else { len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err); } if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { /* XXX figure out if we can create a variant on * qio_channel_yield() that works with AIO contexts * and consider using that in this branch */ qemu_coroutine_yield(); } else if (done) { /* XXX this is needed by nbd_reply_ready. */ qio_channel_wait(ioc, do_read ? G_IO_IN : G_IO_OUT); } else { return -EAGAIN; } continue; } if (len < 0) { TRACE("I/O error: %s", error_get_pretty(local_err)); error_free(local_err); /* XXX handle Error objects */ done = -EIO; goto cleanup; } if (do_read && len == 0) { break; } iov_discard_front(&local_iov, &nlocal_iov, len); done += len; } cleanup: g_free(local_iov_head); return done; }
int qio_channel_readv_all_eof(QIOChannel *ioc, const struct iovec *iov, size_t niov, Error **errp) { int ret = -1; struct iovec *local_iov = g_new(struct iovec, niov); struct iovec *local_iov_head = local_iov; unsigned int nlocal_iov = niov; bool partial = false; nlocal_iov = iov_copy(local_iov, nlocal_iov, iov, niov, 0, iov_size(iov, niov)); while (nlocal_iov > 0) { ssize_t len; len = qio_channel_readv(ioc, local_iov, nlocal_iov, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, G_IO_IN); } else { qio_channel_wait(ioc, G_IO_IN); } continue; } else if (len < 0) { goto cleanup; } else if (len == 0) { if (partial) { error_setg(errp, "Unexpected end-of-file before all bytes were read"); } else { ret = 0; } goto cleanup; } partial = true; iov_discard_front(&local_iov, &nlocal_iov, len); } ret = 1; cleanup: g_free(local_iov_head); return ret; }
static int virtio_crypto_cipher_session_helper(VirtIODevice *vdev, CryptoDevBackendSymSessionInfo *info, struct virtio_crypto_cipher_session_para *cipher_para, struct iovec **iov, unsigned int *out_num) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); unsigned int num = *out_num; info->cipher_alg = ldl_le_p(&cipher_para->algo); info->key_len = ldl_le_p(&cipher_para->keylen); info->direction = ldl_le_p(&cipher_para->op); DPRINTF("cipher_alg=%" PRIu32 ", info->direction=%" PRIu32 "\n", info->cipher_alg, info->direction); if (info->key_len > vcrypto->conf.max_cipher_key_len) { error_report("virtio-crypto length of cipher key is too big: %u", info->key_len); return -VIRTIO_CRYPTO_ERR; } /* Get cipher key */ if (info->key_len > 0) { size_t s; DPRINTF("keylen=%" PRIu32 "\n", info->key_len); info->cipher_key = g_malloc(info->key_len); s = iov_to_buf(*iov, num, 0, info->cipher_key, info->key_len); if (unlikely(s != info->key_len)) { virtio_error(vdev, "virtio-crypto cipher key incorrect"); return -EFAULT; } iov_discard_front(iov, &num, info->key_len); *out_num = num; } return 0; }
int qio_channel_writev_all(QIOChannel *ioc, const struct iovec *iov, size_t niov, Error **errp) { int ret = -1; struct iovec *local_iov = g_new(struct iovec, niov); struct iovec *local_iov_head = local_iov; unsigned int nlocal_iov = niov; nlocal_iov = iov_copy(local_iov, nlocal_iov, iov, niov, 0, iov_size(iov, niov)); while (nlocal_iov > 0) { ssize_t len; len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, G_IO_OUT); } else { qio_channel_wait(ioc, G_IO_OUT); } continue; } if (len < 0) { goto cleanup; } iov_discard_front(&local_iov, &nlocal_iov, len); } ret = 0; cleanup: g_free(local_iov_head); return ret; }
void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; struct iovec *in_iov = req->elem->in_sg; struct iovec *iov = req->elem->out_sg; unsigned in_num = req->elem->in_num; unsigned out_num = req->elem->out_num; if (req->elem->out_num < 1 || req->elem->in_num < 1) { error_report("virtio-blk missing headers"); exit(1); } if (unlikely(iov_to_buf(iov, out_num, 0, &req->out, sizeof(req->out)) != sizeof(req->out))) { error_report("virtio-blk request outhdr too short"); exit(1); } iov_discard_front(&iov, &out_num, sizeof(req->out)); if (in_num < 1 || in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { error_report("virtio-blk request inhdr too short"); exit(1); } req->in = (void *)in_iov[in_num - 1].iov_base + in_iov[in_num - 1].iov_len - sizeof(struct virtio_blk_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); if (type & VIRTIO_BLK_T_FLUSH) { virtio_blk_handle_flush(req, mrb); } else if (type & VIRTIO_BLK_T_SCSI_CMD) { virtio_blk_handle_scsi(req); } else if (type & VIRTIO_BLK_T_GET_ID) { VirtIOBlock *s = req->dev; /* * NB: per existing s/n string convention the string is * terminated by '\0' only when shorter than buffer. */ strncpy(req->elem->in_sg[0].iov_base, s->blk.serial ? s->blk.serial : "", MIN(req->elem->in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); virtio_blk_free_request(req); } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem->out_sg[1], req->elem->out_num - 1); virtio_blk_handle_write(req, mrb); } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) { /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */ qemu_iovec_init_external(&req->qiov, &req->elem->in_sg[0], req->elem->in_num - 1); virtio_blk_handle_read(req); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); virtio_blk_free_request(req); } }
static int64_t 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) { VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); CryptoDevBackendSymSessionInfo info; int64_t session_id; 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_type = op_type; info.op_code = opcode; if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { ret = virtio_crypto_cipher_session_helper(vdev, &info, &sess_req->u.cipher.para, &iov, &out_num); if (ret < 0) { goto err; } } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { size_t s; /* cipher part */ ret = virtio_crypto_cipher_session_helper(vdev, &info, &sess_req->u.chain.para.cipher_param, &iov, &out_num); if (ret < 0) { goto err; } /* hash part */ info.alg_chain_order = ldl_le_p( &sess_req->u.chain.para.alg_chain_order); info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); info.auth_key_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.auth_key_len); info.hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.hash_result_len); if (info.auth_key_len > vcrypto->conf.max_auth_key_len) { error_report("virtio-crypto length of auth key is too big: %u", info.auth_key_len); ret = -VIRTIO_CRYPTO_ERR; goto err; } /* get auth key */ if (info.auth_key_len > 0) { DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len); info.auth_key = g_malloc(info.auth_key_len); s = iov_to_buf(iov, out_num, 0, info.auth_key, info.auth_key_len); if (unlikely(s != info.auth_key_len)) { virtio_error(vdev, "virtio-crypto authenticated key incorrect"); ret = -EFAULT; goto err; } iov_discard_front(&iov, &out_num, info.auth_key_len); } } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { info.hash_alg = ldl_le_p( &sess_req->u.chain.para.u.hash_param.algo); info.hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.hash_param.hash_result_len); } else { /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */ error_report("unsupported hash mode"); ret = -VIRTIO_CRYPTO_NOTSUPP; goto err; } } else { /* VIRTIO_CRYPTO_SYM_OP_NONE */ error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE"); ret = -VIRTIO_CRYPTO_NOTSUPP; goto err; } queue_index = virtio_crypto_vq2q(queue_id); session_id = cryptodev_backend_sym_create_session( vcrypto->cryptodev, &info, queue_index, &local_err); if (session_id >= 0) { DPRINTF("create session_id=%" PRIu64 " successfully\n", session_id); ret = session_id; } else { if (local_err) { error_report_err(local_err); } ret = -VIRTIO_CRYPTO_ERR; } err: g_free(info.cipher_key); g_free(info.auth_key); return ret; }
static int virtio_crypto_handle_request(VirtIOCryptoReq *request) { VirtIOCrypto *vcrypto = request->vcrypto; VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); VirtQueueElement *elem = &request->elem; int queue_index = virtio_crypto_vq2q(virtio_get_queue_index(request->vq)); struct virtio_crypto_op_data_req req; int ret; struct iovec *in_iov; struct iovec *out_iov; unsigned in_num; unsigned out_num; uint32_t opcode; uint8_t status = VIRTIO_CRYPTO_ERR; uint64_t session_id; CryptoDevBackendSymOpInfo *sym_op_info = NULL; Error *local_err = NULL; if (elem->out_num < 1 || elem->in_num < 1) { virtio_error(vdev, "virtio-crypto dataq missing headers"); return -1; } out_num = elem->out_num; out_iov = elem->out_sg; in_num = elem->in_num; in_iov = elem->in_sg; if (unlikely(iov_to_buf(out_iov, out_num, 0, &req, sizeof(req)) != sizeof(req))) { virtio_error(vdev, "virtio-crypto request outhdr too short"); return -1; } iov_discard_front(&out_iov, &out_num, sizeof(req)); if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_crypto_inhdr)) { virtio_error(vdev, "virtio-crypto request inhdr too short"); return -1; } /* We always touch the last byte, so just see how big in_iov is. */ request->in_len = iov_size(in_iov, in_num); request->in = (void *)in_iov[in_num - 1].iov_base + in_iov[in_num - 1].iov_len - sizeof(struct virtio_crypto_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_crypto_inhdr)); /* * The length of operation result, including dest_data * and digest_result if exists. */ request->in_num = in_num; request->in_iov = in_iov; opcode = ldl_le_p(&req.header.opcode); session_id = ldq_le_p(&req.header.session_id); switch (opcode) { case VIRTIO_CRYPTO_CIPHER_ENCRYPT: case VIRTIO_CRYPTO_CIPHER_DECRYPT: ret = virtio_crypto_handle_sym_req(vcrypto, &req.u.sym_req, &sym_op_info, out_iov, out_num); /* Serious errors, need to reset virtio crypto device */ if (ret == -EFAULT) { return -1; } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) { virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); virtio_crypto_free_request(request); } else { sym_op_info->session_id = session_id; /* Set request's parameter */ request->flags = CRYPTODEV_BACKEND_ALG_SYM; request->u.sym_op_info = sym_op_info; 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); } break; case VIRTIO_CRYPTO_HASH: case VIRTIO_CRYPTO_MAC: case VIRTIO_CRYPTO_AEAD_ENCRYPT: case VIRTIO_CRYPTO_AEAD_DECRYPT: default: error_report("virtio-crypto unsupported dataq opcode: %u", opcode); virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); virtio_crypto_free_request(request); } return 0; }
static CryptoDevBackendSymOpInfo * virtio_crypto_sym_op_helper(VirtIODevice *vdev, struct virtio_crypto_cipher_para *cipher_para, struct virtio_crypto_alg_chain_data_para *alg_chain_para, struct iovec *iov, unsigned int out_num) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); CryptoDevBackendSymOpInfo *op_info; uint32_t src_len = 0, dst_len = 0; uint32_t iv_len = 0; uint32_t aad_len = 0, hash_result_len = 0; uint32_t hash_start_src_offset = 0, len_to_hash = 0; uint32_t cipher_start_src_offset = 0, len_to_cipher = 0; uint64_t max_len, curr_size = 0; size_t s; /* Plain cipher */ if (cipher_para) { iv_len = ldl_le_p(&cipher_para->iv_len); src_len = ldl_le_p(&cipher_para->src_data_len); dst_len = ldl_le_p(&cipher_para->dst_data_len); } else if (alg_chain_para) { /* Algorithm chain */ iv_len = ldl_le_p(&alg_chain_para->iv_len); src_len = ldl_le_p(&alg_chain_para->src_data_len); dst_len = ldl_le_p(&alg_chain_para->dst_data_len); aad_len = ldl_le_p(&alg_chain_para->aad_len); hash_result_len = ldl_le_p(&alg_chain_para->hash_result_len); hash_start_src_offset = ldl_le_p( &alg_chain_para->hash_start_src_offset); cipher_start_src_offset = ldl_le_p( &alg_chain_para->cipher_start_src_offset); len_to_cipher = ldl_le_p(&alg_chain_para->len_to_cipher); len_to_hash = ldl_le_p(&alg_chain_para->len_to_hash); } else { return NULL; } max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len; if (unlikely(max_len > vcrypto->conf.max_size)) { virtio_error(vdev, "virtio-crypto too big length"); return NULL; } op_info = g_malloc0(sizeof(CryptoDevBackendSymOpInfo) + max_len); op_info->iv_len = iv_len; op_info->src_len = src_len; op_info->dst_len = dst_len; op_info->aad_len = aad_len; op_info->digest_result_len = hash_result_len; op_info->hash_start_src_offset = hash_start_src_offset; op_info->len_to_hash = len_to_hash; op_info->cipher_start_src_offset = cipher_start_src_offset; op_info->len_to_cipher = len_to_cipher; /* Handle the initilization vector */ if (op_info->iv_len > 0) { DPRINTF("iv_len=%" PRIu32 "\n", op_info->iv_len); op_info->iv = op_info->data + curr_size; s = iov_to_buf(iov, out_num, 0, op_info->iv, op_info->iv_len); if (unlikely(s != op_info->iv_len)) { virtio_error(vdev, "virtio-crypto iv incorrect"); goto err; } iov_discard_front(&iov, &out_num, op_info->iv_len); curr_size += op_info->iv_len; } /* Handle additional authentication data if exists */ if (op_info->aad_len > 0) { DPRINTF("aad_len=%" PRIu32 "\n", op_info->aad_len); op_info->aad_data = op_info->data + curr_size; s = iov_to_buf(iov, out_num, 0, op_info->aad_data, op_info->aad_len); if (unlikely(s != op_info->aad_len)) { virtio_error(vdev, "virtio-crypto additional auth data incorrect"); goto err; } iov_discard_front(&iov, &out_num, op_info->aad_len); curr_size += op_info->aad_len; } /* Handle the source data */ if (op_info->src_len > 0) { DPRINTF("src_len=%" PRIu32 "\n", op_info->src_len); op_info->src = op_info->data + curr_size; s = iov_to_buf(iov, out_num, 0, op_info->src, op_info->src_len); if (unlikely(s != op_info->src_len)) { virtio_error(vdev, "virtio-crypto source data incorrect"); goto err; } iov_discard_front(&iov, &out_num, op_info->src_len); curr_size += op_info->src_len; } /* Handle the destination data */ op_info->dst = op_info->data + curr_size; curr_size += op_info->dst_len; DPRINTF("dst_len=%" PRIu32 "\n", op_info->dst_len); /* Handle the hash digest result */ if (hash_result_len > 0) { DPRINTF("hash_result_len=%" PRIu32 "\n", hash_result_len); op_info->digest_result = op_info->data + curr_size; } return op_info; err: g_free(op_info); return NULL; }
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; unsigned out_num; uint32_t queue_id; uint32_t opcode; struct virtio_crypto_session_input input; int64_t session_id; uint8_t status; size_t s; for (;;) { elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { break; } if (elem->out_num < 1 || elem->in_num < 1) { virtio_error(vdev, "virtio-crypto ctrl missing headers"); virtqueue_detach_element(vq, elem, 0); g_free(elem); break; } out_num = elem->out_num; out_iov = elem->out_sg; in_num = elem->in_num; in_iov = elem->in_sg; if (unlikely(iov_to_buf(out_iov, out_num, 0, &ctrl, sizeof(ctrl)) != sizeof(ctrl))) { virtio_error(vdev, "virtio-crypto request ctrl_hdr too short"); virtqueue_detach_element(vq, elem, 0); g_free(elem); break; } iov_discard_front(&out_iov, &out_num, sizeof(ctrl)); opcode = ldl_le_p(&ctrl.header.opcode); queue_id = ldl_le_p(&ctrl.header.queue_id); switch (opcode) { case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: memset(&input, 0, sizeof(input)); session_id = virtio_crypto_create_sym_session(vcrypto, &ctrl.u.sym_create_session, queue_id, opcode, out_iov, out_num); /* 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; } virtqueue_push(vq, elem, sizeof(input)); virtio_notify(vdev, vq); 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: 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; } 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: error_report("virtio-crypto unsupported ctrl opcode: %d", opcode); memset(&input, 0, sizeof(input)); 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; } virtqueue_push(vq, elem, sizeof(input)); virtio_notify(vdev, vq); break; } /* end switch case */ g_free(elem); } /* end for loop */ }
static void test_discard_front(void) { struct iovec *iov; struct iovec *iov_tmp; unsigned int iov_cnt; unsigned int iov_cnt_tmp; void *old_base; size_t size; size_t ret; /* Discard zero bytes */ iov_random(&iov, &iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, 0); g_assert(ret == 0); g_assert(iov_tmp == iov); g_assert(iov_cnt_tmp == iov_cnt); iov_free(iov, iov_cnt); /* Discard more bytes than vector size */ iov_random(&iov, &iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size + 1); g_assert(ret == size); g_assert(iov_cnt_tmp == 0); iov_free(iov, iov_cnt); /* Discard entire vector */ iov_random(&iov, &iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); g_assert(ret == size); g_assert(iov_cnt_tmp == 0); iov_free(iov, iov_cnt); /* Discard within first element */ iov_random(&iov, &iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; old_base = iov->iov_base; size = g_test_rand_int_range(1, iov->iov_len); ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); g_assert(ret == size); g_assert(iov_tmp == iov); g_assert(iov_cnt_tmp == iov_cnt); g_assert(iov_tmp->iov_base == old_base + size); iov_tmp->iov_base = old_base; /* undo before g_free() */ iov_free(iov, iov_cnt); /* Discard entire first element */ iov_random(&iov, &iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, iov->iov_len); g_assert(ret == iov->iov_len); g_assert(iov_tmp == iov + 1); g_assert(iov_cnt_tmp == iov_cnt - 1); iov_free(iov, iov_cnt); /* Discard within second element */ iov_random(&iov, &iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; old_base = iov[1].iov_base; size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); g_assert(ret == size); g_assert(iov_tmp == iov + 1); g_assert(iov_cnt_tmp == iov_cnt - 1); g_assert(iov_tmp->iov_base == old_base + (size - iov->iov_len)); iov_tmp->iov_base = old_base; /* undo before g_free() */ iov_free(iov, iov_cnt); }
static void vubr_backend_recv_cb(int sock, void *ctx) { VubrDev *vubr = (VubrDev *) ctx; VuDev *dev = &vubr->vudev; VuVirtq *vq = vu_get_queue(dev, 0); VuVirtqElement *elem = NULL; struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; struct virtio_net_hdr_mrg_rxbuf mhdr; unsigned mhdr_cnt = 0; int hdrlen = vubr->hdrlen; int i = 0; struct virtio_net_hdr hdr = { .flags = 0, .gso_type = VIRTIO_NET_HDR_GSO_NONE }; DPRINT("\n\n *** IN UDP RECEIVE CALLBACK ***\n\n"); DPRINT(" hdrlen = %d\n", hdrlen); if (!vu_queue_enabled(dev, vq) || !vu_queue_started(dev, vq) || !vu_queue_avail_bytes(dev, vq, hdrlen, 0)) { DPRINT("Got UDP packet, but no available descriptors on RX virtq.\n"); return; } while (1) { struct iovec *sg; ssize_t ret, total = 0; unsigned int num; elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); if (!elem) { break; } if (elem->in_num < 1) { fprintf(stderr, "virtio-net contains no in buffers\n"); break; } sg = elem->in_sg; num = elem->in_num; if (i == 0) { if (hdrlen == 12) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), sg, elem->in_num, offsetof(typeof(mhdr), num_buffers), sizeof(mhdr.num_buffers)); } iov_from_buf(sg, elem->in_num, 0, &hdr, sizeof hdr); total += hdrlen; ret = iov_discard_front(&sg, &num, hdrlen); assert(ret == hdrlen); } struct msghdr msg = { .msg_name = (struct sockaddr *) &vubr->backend_udp_dest, .msg_namelen = sizeof(struct sockaddr_in), .msg_iov = sg, .msg_iovlen = num, .msg_flags = MSG_DONTWAIT, }; do { ret = recvmsg(vubr->backend_udp_sock, &msg, 0); } while (ret == -1 && (errno == EINTR)); if (i == 0) { iov_restore_front(elem->in_sg, sg, hdrlen); } if (ret == -1) { if (errno == EWOULDBLOCK) { vu_queue_rewind(dev, vq, 1); break; } vubr_die("recvmsg()"); } total += ret; iov_truncate(elem->in_sg, elem->in_num, total); vu_queue_fill(dev, vq, elem, total, i++); free(elem); elem = NULL; break; /* could loop if DONTWAIT worked? */ } if (mhdr_cnt) { mhdr.num_buffers = i; iov_from_buf(mhdr_sg, mhdr_cnt, 0, &mhdr.num_buffers, sizeof mhdr.num_buffers); } vu_queue_flush(dev, vq, i); vu_queue_notify(dev, vq); free(elem); } static void vubr_receive_cb(int sock, void *ctx) { VubrDev *vubr = (VubrDev *)ctx; if (!vu_dispatch(&vubr->vudev)) { fprintf(stderr, "Error while dispatching\n"); } } typedef struct WatchData { VuDev *dev; vu_watch_cb cb; void *data; } WatchData; static void watch_cb(int sock, void *ctx) { struct WatchData *wd = ctx; wd->cb(wd->dev, VU_WATCH_IN, wd->data); } static void vubr_set_watch(VuDev *dev, int fd, int condition, vu_watch_cb cb, void *data) { VubrDev *vubr = container_of(dev, VubrDev, vudev); static WatchData watches[FD_SETSIZE]; struct WatchData *wd = &watches[fd]; wd->cb = cb; wd->data = data; wd->dev = dev; dispatcher_add(&vubr->dispatcher, fd, wd, watch_cb); }