Loading block/qcow2.c +80 −127 Original line number Diff line number Diff line Loading @@ -376,83 +376,77 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, return n1; } typedef struct QCowAIOCB { BlockDriverState *bs; int64_t sector_num; QEMUIOVector *qiov; int remaining_sectors; uint64_t bytes_done; uint8_t *cluster_data; QEMUIOVector hd_qiov; } QCowAIOCB; /* * Returns 0 when the request is completed successfully, 1 when there is still * a part left to do and -errno in error cases. */ static int qcow2_aio_read_cb(QCowAIOCB *acb) static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { BlockDriverState *bs = acb->bs; BDRVQcowState *s = bs->opaque; int index_in_cluster, n1; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ uint64_t cluster_offset = 0; uint64_t bytes_done = 0; QEMUIOVector hd_qiov; uint8_t *cluster_data = NULL; qemu_iovec_init(&hd_qiov, qiov->niov); qemu_co_mutex_lock(&s->lock); while (acb->remaining_sectors != 0) { while (remaining_sectors != 0) { /* prepare next request */ cur_nr_sectors = acb->remaining_sectors; cur_nr_sectors = remaining_sectors; if (s->crypt_method) { cur_nr_sectors = MIN(cur_nr_sectors, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); } ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9, ret = qcow2_get_cluster_offset(bs, sector_num << 9, &cur_nr_sectors, &cluster_offset); if (ret < 0) { return ret; goto fail; } index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, qemu_iovec_reset(&hd_qiov); qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); if (!cluster_offset) { if (bs->backing_hd) { /* read from the base image */ n1 = qcow2_backing_read1(bs->backing_hd, &acb->hd_qiov, acb->sector_num, cur_nr_sectors); n1 = qcow2_backing_read1(bs->backing_hd, &hd_qiov, sector_num, cur_nr_sectors); if (n1 > 0) { BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_readv(bs->backing_hd, acb->sector_num, n1, &acb->hd_qiov); ret = bdrv_co_readv(bs->backing_hd, sector_num, n1, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { return ret; goto fail; } } } else { /* Note: in this case, no need to wait */ qemu_iovec_memset(&acb->hd_qiov, 0, 512 * cur_nr_sectors); qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); if (ret < 0) { return ret; goto fail; } qemu_iovec_from_buffer(&acb->hd_qiov, qemu_iovec_from_buffer(&hd_qiov, s->cluster_cache + index_in_cluster * 512, 512 * cur_nr_sectors); } else { if ((cluster_offset & 511) != 0) { return -EIO; ret = -EIO; goto fail; } if (s->crypt_method) { Loading @@ -460,15 +454,15 @@ static int qcow2_aio_read_cb(QCowAIOCB *acb) * For encrypted images, read everything into a temporary * contiguous buffer on which the AES functions can work. */ if (!acb->cluster_data) { acb->cluster_data = if (!cluster_data) { cluster_data = g_malloc0(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); } assert(cur_nr_sectors <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_add(&acb->hd_qiov, acb->cluster_data, qemu_iovec_reset(&hd_qiov); qemu_iovec_add(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } Loading @@ -476,63 +470,32 @@ static int qcow2_aio_read_cb(QCowAIOCB *acb) qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_readv(bs->file, (cluster_offset >> 9) + index_in_cluster, cur_nr_sectors, &acb->hd_qiov); cur_nr_sectors, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { return ret; goto fail; } if (s->crypt_method) { qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, qcow2_encrypt_sectors(s, sector_num, cluster_data, cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); qemu_iovec_reset(&hd_qiov); qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); qemu_iovec_from_buffer(&acb->hd_qiov, acb->cluster_data, qemu_iovec_from_buffer(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } } acb->remaining_sectors -= cur_nr_sectors; acb->sector_num += cur_nr_sectors; acb->bytes_done += cur_nr_sectors * 512; remaining_sectors -= cur_nr_sectors; sector_num += cur_nr_sectors; bytes_done += cur_nr_sectors * 512; } ret = 0; return 0; } static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque, QCowAIOCB *acb) { memset(acb, 0, sizeof(*acb)); acb->bs = bs; acb->sector_num = sector_num; acb->qiov = qiov; qemu_iovec_init(&acb->hd_qiov, qiov->niov); acb->bytes_done = 0; acb->remaining_sectors = nb_sectors; return acb; } static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; QCowAIOCB acb; int ret; qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, &acb); qemu_co_mutex_lock(&s->lock); do { ret = qcow2_aio_read_cb(&acb); } while (ret > 0); fail: qemu_co_mutex_unlock(&s->lock); qemu_iovec_destroy(&acb.hd_qiov); qemu_iovec_destroy(&hd_qiov); return ret; } Loading @@ -552,13 +515,11 @@ static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m) } } /* * Returns 0 when the request is completed successfully, 1 when there is still * a part left to do and -errno in error cases. */ static int qcow2_aio_write_cb(QCowAIOCB *acb) static int qcow2_co_writev(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { BlockDriverState *bs = acb->bs; BDRVQcowState *s = bs->opaque; int index_in_cluster; int n_end; Loading @@ -566,47 +527,56 @@ static int qcow2_aio_write_cb(QCowAIOCB *acb) int cur_nr_sectors; /* number of sectors in current iteration */ QCowL2Meta l2meta; uint64_t cluster_offset; QEMUIOVector hd_qiov; uint64_t bytes_done = 0; uint8_t *cluster_data = NULL; l2meta.nb_clusters = 0; qemu_co_queue_init(&l2meta.dependent_requests); while (acb->remaining_sectors != 0) { qemu_iovec_init(&hd_qiov, qiov->niov); index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); n_end = index_in_cluster + acb->remaining_sectors; s->cluster_cache_offset = -1; /* disable compressed cache */ qemu_co_mutex_lock(&s->lock); while (remaining_sectors != 0) { index_in_cluster = sector_num & (s->cluster_sectors - 1); n_end = index_in_cluster + remaining_sectors; if (s->crypt_method && n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) { n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; } ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9, ret = qcow2_alloc_cluster_offset(bs, sector_num << 9, index_in_cluster, n_end, &cur_nr_sectors, &l2meta); if (ret < 0) { return ret; goto fail; } cluster_offset = l2meta.cluster_offset; assert((cluster_offset & 511) == 0); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, qemu_iovec_reset(&hd_qiov); qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); if (s->crypt_method) { if (!acb->cluster_data) { acb->cluster_data = g_malloc0(QCOW_MAX_CRYPT_CLUSTERS * if (!cluster_data) { cluster_data = g_malloc0(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); } assert(acb->hd_qiov.size <= assert(hd_qiov.size <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); qemu_iovec_to_buffer(&acb->hd_qiov, acb->cluster_data); qemu_iovec_to_buffer(&hd_qiov, cluster_data); qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); qcow2_encrypt_sectors(s, sector_num, cluster_data, cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_add(&acb->hd_qiov, acb->cluster_data, qemu_iovec_reset(&hd_qiov); qemu_iovec_add(&hd_qiov, cluster_data, cur_nr_sectors * 512); } Loading @@ -614,10 +584,10 @@ static int qcow2_aio_write_cb(QCowAIOCB *acb) qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + index_in_cluster, cur_nr_sectors, &acb->hd_qiov); cur_nr_sectors, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { return ret; goto fail; } ret = qcow2_alloc_cluster_link_l2(bs, &l2meta); Loading @@ -625,36 +595,19 @@ static int qcow2_aio_write_cb(QCowAIOCB *acb) run_dependent_requests(s, &l2meta); if (ret < 0) { return ret; } acb->remaining_sectors -= cur_nr_sectors; acb->sector_num += cur_nr_sectors; acb->bytes_done += cur_nr_sectors * 512; goto fail; } return 0; remaining_sectors -= cur_nr_sectors; sector_num += cur_nr_sectors; bytes_done += cur_nr_sectors * 512; } ret = 0; static int qcow2_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; QCowAIOCB acb; int ret; qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, &acb); s->cluster_cache_offset = -1; /* disable compressed cache */ qemu_co_mutex_lock(&s->lock); do { ret = qcow2_aio_write_cb(&acb); } while (ret > 0); fail: qemu_co_mutex_unlock(&s->lock); qemu_iovec_destroy(&acb.hd_qiov); qemu_iovec_destroy(&hd_qiov); return ret; } Loading Loading
block/qcow2.c +80 −127 Original line number Diff line number Diff line Loading @@ -376,83 +376,77 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, return n1; } typedef struct QCowAIOCB { BlockDriverState *bs; int64_t sector_num; QEMUIOVector *qiov; int remaining_sectors; uint64_t bytes_done; uint8_t *cluster_data; QEMUIOVector hd_qiov; } QCowAIOCB; /* * Returns 0 when the request is completed successfully, 1 when there is still * a part left to do and -errno in error cases. */ static int qcow2_aio_read_cb(QCowAIOCB *acb) static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { BlockDriverState *bs = acb->bs; BDRVQcowState *s = bs->opaque; int index_in_cluster, n1; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ uint64_t cluster_offset = 0; uint64_t bytes_done = 0; QEMUIOVector hd_qiov; uint8_t *cluster_data = NULL; qemu_iovec_init(&hd_qiov, qiov->niov); qemu_co_mutex_lock(&s->lock); while (acb->remaining_sectors != 0) { while (remaining_sectors != 0) { /* prepare next request */ cur_nr_sectors = acb->remaining_sectors; cur_nr_sectors = remaining_sectors; if (s->crypt_method) { cur_nr_sectors = MIN(cur_nr_sectors, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); } ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9, ret = qcow2_get_cluster_offset(bs, sector_num << 9, &cur_nr_sectors, &cluster_offset); if (ret < 0) { return ret; goto fail; } index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, qemu_iovec_reset(&hd_qiov); qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); if (!cluster_offset) { if (bs->backing_hd) { /* read from the base image */ n1 = qcow2_backing_read1(bs->backing_hd, &acb->hd_qiov, acb->sector_num, cur_nr_sectors); n1 = qcow2_backing_read1(bs->backing_hd, &hd_qiov, sector_num, cur_nr_sectors); if (n1 > 0) { BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_readv(bs->backing_hd, acb->sector_num, n1, &acb->hd_qiov); ret = bdrv_co_readv(bs->backing_hd, sector_num, n1, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { return ret; goto fail; } } } else { /* Note: in this case, no need to wait */ qemu_iovec_memset(&acb->hd_qiov, 0, 512 * cur_nr_sectors); qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); if (ret < 0) { return ret; goto fail; } qemu_iovec_from_buffer(&acb->hd_qiov, qemu_iovec_from_buffer(&hd_qiov, s->cluster_cache + index_in_cluster * 512, 512 * cur_nr_sectors); } else { if ((cluster_offset & 511) != 0) { return -EIO; ret = -EIO; goto fail; } if (s->crypt_method) { Loading @@ -460,15 +454,15 @@ static int qcow2_aio_read_cb(QCowAIOCB *acb) * For encrypted images, read everything into a temporary * contiguous buffer on which the AES functions can work. */ if (!acb->cluster_data) { acb->cluster_data = if (!cluster_data) { cluster_data = g_malloc0(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); } assert(cur_nr_sectors <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_add(&acb->hd_qiov, acb->cluster_data, qemu_iovec_reset(&hd_qiov); qemu_iovec_add(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } Loading @@ -476,63 +470,32 @@ static int qcow2_aio_read_cb(QCowAIOCB *acb) qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_readv(bs->file, (cluster_offset >> 9) + index_in_cluster, cur_nr_sectors, &acb->hd_qiov); cur_nr_sectors, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { return ret; goto fail; } if (s->crypt_method) { qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, qcow2_encrypt_sectors(s, sector_num, cluster_data, cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); qemu_iovec_reset(&hd_qiov); qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); qemu_iovec_from_buffer(&acb->hd_qiov, acb->cluster_data, qemu_iovec_from_buffer(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } } acb->remaining_sectors -= cur_nr_sectors; acb->sector_num += cur_nr_sectors; acb->bytes_done += cur_nr_sectors * 512; remaining_sectors -= cur_nr_sectors; sector_num += cur_nr_sectors; bytes_done += cur_nr_sectors * 512; } ret = 0; return 0; } static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque, QCowAIOCB *acb) { memset(acb, 0, sizeof(*acb)); acb->bs = bs; acb->sector_num = sector_num; acb->qiov = qiov; qemu_iovec_init(&acb->hd_qiov, qiov->niov); acb->bytes_done = 0; acb->remaining_sectors = nb_sectors; return acb; } static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; QCowAIOCB acb; int ret; qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, &acb); qemu_co_mutex_lock(&s->lock); do { ret = qcow2_aio_read_cb(&acb); } while (ret > 0); fail: qemu_co_mutex_unlock(&s->lock); qemu_iovec_destroy(&acb.hd_qiov); qemu_iovec_destroy(&hd_qiov); return ret; } Loading @@ -552,13 +515,11 @@ static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m) } } /* * Returns 0 when the request is completed successfully, 1 when there is still * a part left to do and -errno in error cases. */ static int qcow2_aio_write_cb(QCowAIOCB *acb) static int qcow2_co_writev(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { BlockDriverState *bs = acb->bs; BDRVQcowState *s = bs->opaque; int index_in_cluster; int n_end; Loading @@ -566,47 +527,56 @@ static int qcow2_aio_write_cb(QCowAIOCB *acb) int cur_nr_sectors; /* number of sectors in current iteration */ QCowL2Meta l2meta; uint64_t cluster_offset; QEMUIOVector hd_qiov; uint64_t bytes_done = 0; uint8_t *cluster_data = NULL; l2meta.nb_clusters = 0; qemu_co_queue_init(&l2meta.dependent_requests); while (acb->remaining_sectors != 0) { qemu_iovec_init(&hd_qiov, qiov->niov); index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); n_end = index_in_cluster + acb->remaining_sectors; s->cluster_cache_offset = -1; /* disable compressed cache */ qemu_co_mutex_lock(&s->lock); while (remaining_sectors != 0) { index_in_cluster = sector_num & (s->cluster_sectors - 1); n_end = index_in_cluster + remaining_sectors; if (s->crypt_method && n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) { n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; } ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9, ret = qcow2_alloc_cluster_offset(bs, sector_num << 9, index_in_cluster, n_end, &cur_nr_sectors, &l2meta); if (ret < 0) { return ret; goto fail; } cluster_offset = l2meta.cluster_offset; assert((cluster_offset & 511) == 0); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, qemu_iovec_reset(&hd_qiov); qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); if (s->crypt_method) { if (!acb->cluster_data) { acb->cluster_data = g_malloc0(QCOW_MAX_CRYPT_CLUSTERS * if (!cluster_data) { cluster_data = g_malloc0(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); } assert(acb->hd_qiov.size <= assert(hd_qiov.size <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); qemu_iovec_to_buffer(&acb->hd_qiov, acb->cluster_data); qemu_iovec_to_buffer(&hd_qiov, cluster_data); qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); qcow2_encrypt_sectors(s, sector_num, cluster_data, cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); qemu_iovec_reset(&acb->hd_qiov); qemu_iovec_add(&acb->hd_qiov, acb->cluster_data, qemu_iovec_reset(&hd_qiov); qemu_iovec_add(&hd_qiov, cluster_data, cur_nr_sectors * 512); } Loading @@ -614,10 +584,10 @@ static int qcow2_aio_write_cb(QCowAIOCB *acb) qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + index_in_cluster, cur_nr_sectors, &acb->hd_qiov); cur_nr_sectors, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { return ret; goto fail; } ret = qcow2_alloc_cluster_link_l2(bs, &l2meta); Loading @@ -625,36 +595,19 @@ static int qcow2_aio_write_cb(QCowAIOCB *acb) run_dependent_requests(s, &l2meta); if (ret < 0) { return ret; } acb->remaining_sectors -= cur_nr_sectors; acb->sector_num += cur_nr_sectors; acb->bytes_done += cur_nr_sectors * 512; goto fail; } return 0; remaining_sectors -= cur_nr_sectors; sector_num += cur_nr_sectors; bytes_done += cur_nr_sectors * 512; } ret = 0; static int qcow2_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; QCowAIOCB acb; int ret; qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, &acb); s->cluster_cache_offset = -1; /* disable compressed cache */ qemu_co_mutex_lock(&s->lock); do { ret = qcow2_aio_write_cb(&acb); } while (ret > 0); fail: qemu_co_mutex_unlock(&s->lock); qemu_iovec_destroy(&acb.hd_qiov); qemu_iovec_destroy(&hd_qiov); return ret; } Loading