/* * Completes an AIO request (calls the callback and frees the ACB). */ static void qemu_laio_process_completion(struct qemu_laio_state *s, struct qemu_laiocb *laiocb) { int ret; s->count--; ret = laiocb->ret; if (ret != -ECANCELED) { if (ret == laiocb->nbytes) { ret = 0; } else if (ret >= 0) { /* Short reads mean EOF, pad with zeros. */ if (laiocb->is_read) { qemu_iovec_memset_skip(laiocb->qiov, 0, laiocb->qiov->size - ret, ret); } else { ret = -EINVAL; } } } laiocb->common.cb(laiocb->common.opaque, ret); qemu_aio_unref(laiocb); }
/* * Completes an AIO request (calls the callback and frees the ACB). */ static void win32_aio_process_completion(QEMUWin32AIOState *s, QEMUWin32AIOCB *waiocb, DWORD count) { int ret; s->count--; if (waiocb->ov.Internal != 0) { ret = -EIO; } else { ret = 0; if (count < waiocb->nbytes) { /* Short reads mean EOF, pad with zeros. */ if (waiocb->is_read) { qemu_iovec_memset(waiocb->qiov, count, 0, waiocb->qiov->size - count); } else { ret = -EINVAL; } } } if (!waiocb->is_linear) { if (ret == 0 && waiocb->is_read) { QEMUIOVector *qiov = waiocb->qiov; iov_from_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); } qemu_vfree(waiocb->buf); } waiocb->common.cb(waiocb->common.opaque, ret); qemu_aio_unref(waiocb); }
static void curl_multi_check_completion(BDRVCURLState *s) { int msgs_in_queue; /* Try to find done transfers, so we can free the easy * handle again. */ for (;;) { CURLMsg *msg; msg = curl_multi_info_read(s->multi, &msgs_in_queue); /* Quit when there are no more completions */ if (!msg) break; if (msg->msg == CURLMSG_DONE) { CURLState *state = NULL; curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **)&state); /* ACBs for successful messages get completed in curl_read_cb */ if (msg->data.result != CURLE_OK) { int i; static int errcount = 100; /* Don't lose the original error message from curl, since * it contains extra data. */ if (errcount > 0) { error_report("curl: %s", state->errmsg); if (--errcount == 0) { error_report("curl: further errors suppressed"); } } for (i = 0; i < CURL_NUM_ACB; i++) { CURLAIOCB *acb = state->acb[i]; if (acb == NULL) { continue; } acb->common.cb(acb->common.opaque, -EPROTO); qemu_aio_unref(acb); state->acb[i] = NULL; } } curl_clean_state(state); break; } } }
BlockAIOCB *win32_aio_submit(BlockDriverState *bs, QEMUWin32AIOState *aio, HANDLE hfile, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque, int type) { struct QEMUWin32AIOCB *waiocb; uint64_t offset = sector_num * 512; DWORD rc; waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); waiocb->nbytes = nb_sectors * 512; waiocb->qiov = qiov; waiocb->is_read = (type == QEMU_AIO_READ); if (qiov->niov > 1) { waiocb->buf = qemu_try_blockalign(bs, qiov->size); if (waiocb->buf == NULL) { goto out; } if (type & QEMU_AIO_WRITE) { iov_to_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); } waiocb->is_linear = false; } else { waiocb->buf = qiov->iov[0].iov_base; waiocb->is_linear = true; } memset(&waiocb->ov, 0, sizeof(waiocb->ov)); waiocb->ov.Offset = (DWORD)offset; waiocb->ov.OffsetHigh = (DWORD)(offset >> 32); waiocb->ov.hEvent = event_notifier_get_handle(&aio->e); aio->count++; if (type & QEMU_AIO_READ) { rc = ReadFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov); } else { rc = WriteFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov); } if(rc == 0 && GetLastError() != ERROR_IO_PENDING) { goto out_dec_count; } return &waiocb->common; out_dec_count: aio->count--; out: qemu_aio_unref(waiocb); return NULL; }
static void rbd_aio_bh_cb(void *opaque) { RBDAIOCB *acb = opaque; if (acb->cmd == RBD_AIO_READ) { qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size); } qemu_vfree(acb->bounce); acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); qemu_bh_delete(acb->bh); acb->bh = NULL; acb->status = 0; qemu_aio_unref(acb); }
static void dma_complete(DMAAIOCB *dbs, int ret) { trace_dma_complete(dbs, ret, dbs->common.cb); dma_blk_unmap(dbs); if (dbs->common.cb) { dbs->common.cb(dbs->common.opaque, ret); } qemu_iovec_destroy(&dbs->iov); if (dbs->bh) { qemu_bh_delete(dbs->bh); dbs->bh = NULL; } qemu_aio_unref(dbs); }
static void vxhs_complete_aio_bh(void *opaque) { VXHSAIOCB *acb = opaque; BlockCompletionFunc *cb = acb->common.cb; void *cb_opaque = acb->common.opaque; int ret = 0; if (acb->err != 0) { trace_vxhs_complete_aio(acb, acb->err); ret = (-EIO); } qemu_aio_unref(acb); cb(cb_opaque, ret); }
BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque, int type) { struct qemu_laio_state *s = aio_ctx; struct qemu_laiocb *laiocb; struct iocb *iocbs; off_t offset = sector_num * 512; laiocb = qemu_aio_get(&laio_aiocb_info, bs, cb, opaque); laiocb->nbytes = nb_sectors * 512; laiocb->ctx = s; laiocb->ret = -EINPROGRESS; laiocb->is_read = (type == QEMU_AIO_READ); laiocb->qiov = qiov; iocbs = &laiocb->iocb; switch (type) { case QEMU_AIO_WRITE: io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); break; case QEMU_AIO_READ: io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); break; /* Currently Linux kernel does not support other operations */ default: fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", __func__, type); goto out_free_aiocb; } io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); if (!s->io_q.plugged) { if (io_submit(s->ctx, 1, &iocbs) < 0) { goto out_free_aiocb; } } else { ioq_enqueue(s, iocbs); } return &laiocb->common; out_free_aiocb: qemu_aio_unref(laiocb); return NULL; }
/* * This allocates QEMU-VXHS callback for each IO * and is passed to QNIO. When QNIO completes the work, * it will be passed back through the callback. */ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset, QEMUIOVector *qiov, uint64_t size, BlockCompletionFunc *cb, void *opaque, VDISKAIOCmd iodir) { VXHSAIOCB *acb = NULL; BDRVVXHSState *s = bs->opaque; int iio_flags = 0; int ret = 0; void *dev_handle = s->vdisk_hostinfo.dev_handle; acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); /* * Initialize VXHSAIOCB. */ acb->err = 0; iio_flags = IIO_FLAG_ASYNC; switch (iodir) { case VDISK_AIO_WRITE: ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, offset, size, iio_flags); break; case VDISK_AIO_READ: ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, offset, size, iio_flags); break; default: trace_vxhs_aio_rw_invalid(iodir); goto errout; } if (ret != 0) { trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset, acb, ret, errno); goto errout; } return &acb->common; errout: qemu_aio_unref(acb); return NULL; }
static void iscsi_bh_cb(void *p) { IscsiAIOCB *acb = p; qemu_bh_delete(acb->bh); g_free(acb->buf); acb->buf = NULL; acb->common.cb(acb->common.opaque, acb->status); if (acb->task != NULL) { scsi_free_scsi_task(acb->task); acb->task = NULL; } qemu_aio_unref(acb); }
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque, int type) { struct qemu_laio_state *s = aio_ctx; struct qemu_laiocb *laiocb; struct iocb *iocbs; off_t offset = sector_num * 512; laiocb = qemu_aio_get(&laio_aiocb_info, bs, cb, opaque); laiocb->nbytes = nb_sectors * 512; laiocb->ctx = s; laiocb->ret = -EINPROGRESS; laiocb->is_read = (type == QEMU_AIO_READ); laiocb->qiov = qiov; iocbs = &laiocb->iocb; switch (type) { case QEMU_AIO_WRITE: io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); break; case QEMU_AIO_READ: io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); break; default: fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", __func__, type); goto out_free_aiocb; } io_set_eventfd(&laiocb->iocb, s->efd); s->count++; if (io_submit(s->ctx, 1, &iocbs) < 0) goto out_dec_count; return &laiocb->common; out_dec_count: s->count--; out_free_aiocb: qemu_aio_unref(laiocb); return NULL; }
/* * This aio completion is being called from rbd_finish_bh() and runs in qemu * BH context. */ static void qemu_rbd_complete_aio(RADOSCB *rcb) { RBDAIOCB *acb = rcb->acb; int64_t r; r = rcb->ret; if (acb->cmd != RBD_AIO_READ) { if (r < 0) { acb->ret = r; acb->error = 1; } else if (!acb->error) { acb->ret = rcb->size; } } else { if (r < 0) { qemu_rbd_memset(rcb, 0); acb->ret = r; acb->error = 1; } else if (r < rcb->size) { qemu_rbd_memset(rcb, r); if (!acb->error) { acb->ret = rcb->size; } } else if (!acb->error) { acb->ret = r; } } g_free(rcb); if (!LIBRBD_USE_IOVEC) { if (acb->cmd == RBD_AIO_READ) { qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size); } qemu_vfree(acb->bounce); } acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); qemu_aio_unref(acb); }
static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) { CURLState *s = ((CURLState*)opaque); size_t realsize = size * nmemb; int i; DPRINTF("CURL: Just reading %zd bytes\n", realsize); if (!s || !s->orig_buf) return 0; if (s->buf_off >= s->buf_len) { /* buffer full, read nothing */ return 0; } realsize = MIN(realsize, s->buf_len - s->buf_off); memcpy(s->orig_buf + s->buf_off, ptr, realsize); s->buf_off += realsize; for(i=0; i<CURL_NUM_ACB; i++) { CURLAIOCB *acb = s->acb[i]; if (!acb) continue; if ((s->buf_off >= acb->end)) { qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start, acb->end - acb->start); acb->common.cb(acb->common.opaque, 0); qemu_aio_unref(acb); s->acb[i] = NULL; } } return realsize; }
static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, int64_t off, QEMUIOVector *qiov, int64_t size, BlockCompletionFunc *cb, void *opaque, RBDAIOCmd cmd) { RBDAIOCB *acb; RADOSCB *rcb = NULL; rbd_completion_t c; int r; BDRVRBDState *s = bs->opaque; acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque); acb->cmd = cmd; acb->qiov = qiov; assert(!qiov || qiov->size == size); rcb = g_new(RADOSCB, 1); if (!LIBRBD_USE_IOVEC) { if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { acb->bounce = NULL; } else { acb->bounce = qemu_try_blockalign(bs, qiov->size); if (acb->bounce == NULL) { goto failed; } } if (cmd == RBD_AIO_WRITE) { qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); } rcb->buf = acb->bounce; } acb->ret = 0; acb->error = 0; acb->s = s; rcb->acb = acb; rcb->s = acb->s; rcb->size = size; r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); if (r < 0) { goto failed; } switch (cmd) { case RBD_AIO_WRITE: #ifdef LIBRBD_SUPPORTS_IOVEC r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c); #else r = rbd_aio_write(s->image, off, size, rcb->buf, c); #endif break; case RBD_AIO_READ: #ifdef LIBRBD_SUPPORTS_IOVEC r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c); #else r = rbd_aio_read(s->image, off, size, rcb->buf, c); #endif break; case RBD_AIO_DISCARD: r = rbd_aio_discard_wrapper(s->image, off, size, c); break; case RBD_AIO_FLUSH: r = rbd_aio_flush_wrapper(s->image, c); break; default: r = -EINVAL; } if (r < 0) { goto failed_completion; } return &acb->common; failed_completion: rbd_aio_release(c); failed: g_free(rcb); if (!LIBRBD_USE_IOVEC) { qemu_vfree(acb->bounce); } qemu_aio_unref(acb); return NULL; }
static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque, RBDAIOCmd cmd) { RBDAIOCB *acb; RADOSCB *rcb; rbd_completion_t c; int64_t off, size; char *buf; int r; BDRVRBDState *s = bs->opaque; acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque); acb->cmd = cmd; acb->qiov = qiov; if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { acb->bounce = NULL; } else { acb->bounce = qemu_blockalign(bs, qiov->size); } acb->ret = 0; acb->error = 0; acb->s = s; acb->bh = NULL; acb->status = -EINPROGRESS; if (cmd == RBD_AIO_WRITE) { qemu_iovec_to_buffer(acb->qiov, acb->bounce); } buf = acb->bounce; off = sector_num * BDRV_SECTOR_SIZE; size = nb_sectors * BDRV_SECTOR_SIZE; s->qemu_aio_count++; /* All the RADOSCB */ rcb = g_malloc(sizeof(RADOSCB)); rcb->done = 0; rcb->acb = acb; rcb->buf = buf; rcb->s = acb->s; rcb->size = size; r = (*librbd.rbd_aio_create_completion)(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); if (r < 0) { goto failed; } switch (cmd) { case RBD_AIO_WRITE: r = (*librbd.rbd_aio_write)(s->image, off, size, buf, c); break; case RBD_AIO_READ: r = (*librbd.rbd_aio_read)(s->image, off, size, buf, c); break; case RBD_AIO_DISCARD: r = (*librbd.rbd_aio_discard)(s->image, off, size, c); break; case RBD_AIO_FLUSH: r = (*librbd.rbd_aio_flush)(s->image, c); break; default: r = -EINVAL; } if (r < 0) { goto failed; } return &acb->common; failed: g_free(rcb); s->qemu_aio_count--; qemu_aio_unref(acb); return NULL; }
static void curl_readv_bh_cb(void *p) { CURLState *state; int running; CURLAIOCB *acb = p; BDRVCURLState *s = acb->common.bs->opaque; qemu_bh_delete(acb->bh); acb->bh = NULL; size_t start = acb->sector_num * SECTOR_SIZE; size_t end; // In case we have the requested data already (e.g. read-ahead), // we can just call the callback and be done. switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) { case FIND_RET_OK: qemu_aio_unref(acb); // fall through case FIND_RET_WAIT: return; default: break; } // No cache found, so let's start a new request state = curl_init_state(acb->common.bs, s); if (!state) { acb->common.cb(acb->common.opaque, -EIO); qemu_aio_unref(acb); return; } acb->start = 0; acb->end = (acb->nb_sectors * SECTOR_SIZE); state->buf_off = 0; g_free(state->orig_buf); state->buf_start = start; state->buf_len = acb->end + s->readahead_size; end = MIN(start + state->buf_len, s->len) - 1; state->orig_buf = g_try_malloc(state->buf_len); if (state->buf_len && state->orig_buf == NULL) { curl_clean_state(state); acb->common.cb(acb->common.opaque, -ENOMEM); qemu_aio_unref(acb); return; } state->acb[0] = acb; snprintf(state->range, 127, "%zd-%zd", start, end); DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n", (acb->nb_sectors * SECTOR_SIZE), start, state->range); curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); curl_multi_add_handle(s->multi, state->curl); /* Tell curl it needs to kick things off */ curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); }