void laio_attach_aio_context(void *s_, AioContext *new_context) { struct qemu_laio_state *s = s_; s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb); }
static void iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) { struct IscsiTask *iTask = opaque; struct scsi_task *task = command_data; iTask->complete = 1; iTask->status = status; iTask->do_retry = 0; iTask->task = task; if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi)); iTask->do_retry = 1; goto out; } if (status != SCSI_STATUS_GOOD) { error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); } out: if (iTask->co) { iTask->bh = aio_bh_new(iTask->iscsilun->aio_context, iscsi_co_generic_bh_cb, iTask); qemu_bh_schedule(iTask->bh); } }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, VirtIOBlockDataPlane **dataplane, Error **errp) { VirtIOBlockDataPlane *s; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; if (!conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, "device is incompatible with dataplane " "(transport does not support notifiers)"); return; } /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { error_prepend(errp, "cannot start dataplane thread: "); return; } s = g_new0(VirtIOBlockDataPlane, 1); s->vdev = vdev; s->conf = conf; if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); error_setg(&s->blocker, "block device is in use by data plane"); blk_op_block_all(conf->conf.blk, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); *dataplane = s; }
static void iscsi_schedule_bh(IscsiAIOCB *acb) { if (acb->bh) { return; } acb->bh = aio_bh_new(acb->iscsilun->aio_context, iscsi_bh_cb, acb); qemu_bh_schedule(acb->bh); }
/* * This is the callback function for rbd_aio_read and _write * * Note: this function is being called from a non qemu thread so * we need to be careful about what we do here. Generally we only * schedule a BH, and do the rest of the io completion handling * from rbd_finish_bh() which runs in a qemu context. */ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) { RBDAIOCB *acb = rcb->acb; rcb->ret = rbd_aio_get_return_value(c); rbd_aio_release(c); acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs), rbd_finish_bh, rcb); qemu_bh_schedule(acb->bh); }
static void archipelago_finish_aiocb(AIORequestData *reqdata) { if (reqdata->aio_cb->ret != reqdata->segreq->total) { reqdata->aio_cb->ret = -EIO; } else if (reqdata->aio_cb->ret == reqdata->segreq->total) { reqdata->aio_cb->ret = 0; } reqdata->aio_cb->bh = aio_bh_new( bdrv_get_aio_context(reqdata->aio_cb->common.bs), qemu_archipelago_complete_aio, reqdata ); qemu_bh_schedule(reqdata->aio_cb->bh); }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, VirtIOBlockDataPlane **dataplane, Error **errp) { VirtIOBlockDataPlane *s; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; if (!conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, "device is incompatible with dataplane " "(transport does not support notifiers)"); return; } /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { error_prepend(errp, "cannot start dataplane thread: "); return; } s = g_new0(VirtIOBlockDataPlane, 1); s->vdev = vdev; s->conf = conf; if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); s->insert_notifier.notify = data_plane_blk_insert_notifier; s->remove_notifier.notify = data_plane_blk_remove_notifier; blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); data_plane_set_up_op_blockers(s); *dataplane = s; }
static BlockAIOCB *curl_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { CURLAIOCB *acb; acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque); acb->qiov = qiov; acb->sector_num = sector_num; acb->nb_sectors = nb_sectors; acb->bh = aio_bh_new(bdrv_get_aio_context(bs), curl_readv_bh_cb, acb); qemu_bh_schedule(acb->bh); return &acb->common; }
static void iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) { struct IscsiTask *iTask = opaque; struct scsi_task *task = command_data; iTask->status = status; iTask->do_retry = 0; iTask->task = task; if (status != SCSI_STATUS_GOOD) { if (iTask->retries++ < ISCSI_CMD_RETRIES) { if (status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi)); iTask->do_retry = 1; goto out; } if (status == SCSI_STATUS_BUSY) { unsigned retry_time = exp_random(iscsi_retry_times[iTask->retries - 1]); error_report("iSCSI Busy (retry #%u in %u ms): %s", iTask->retries, retry_time, iscsi_get_error(iscsi)); aio_timer_init(iTask->iscsilun->aio_context, &iTask->retry_timer, QEMU_CLOCK_REALTIME, SCALE_MS, iscsi_retry_timer_expired, iTask); timer_mod(&iTask->retry_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time); iTask->do_retry = 1; return; } } error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); } out: if (iTask->co) { iTask->bh = aio_bh_new(iTask->iscsilun->aio_context, iscsi_co_generic_bh_cb, iTask); qemu_bh_schedule(iTask->bh); } else { iTask->complete = 1; } }
static void dma_blk_cb(void *opaque, int ret) { DMAAIOCB *dbs = (DMAAIOCB *)opaque; dma_addr_t cur_addr, cur_len; void *mem; trace_dma_blk_cb(dbs, ret); dbs->acb = NULL; dbs->sector_num += dbs->iov.size / 512; if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) { dma_complete(dbs, ret); return; } dma_blk_unmap(dbs); while (dbs->sg_cur_index < dbs->sg->nsg) { cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte; cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte; mem = dma_memory_map(dbs->sg->as, cur_addr, &cur_len, dbs->dir); if (!mem) break; qemu_iovec_add(&dbs->iov, mem, cur_len); dbs->sg_cur_byte += cur_len; if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) { dbs->sg_cur_byte = 0; ++dbs->sg_cur_index; } } if (dbs->iov.size == 0) { trace_dma_map_wait(dbs); dbs->bh = aio_bh_new(blk_get_aio_context(dbs->blk), reschedule_dma, dbs); cpu_register_map_client(dbs->bh); return; } if (dbs->iov.size & ~BDRV_SECTOR_MASK) { qemu_iovec_discard_back(&dbs->iov, dbs->iov.size & ~BDRV_SECTOR_MASK); } dbs->acb = dbs->io_func(dbs->blk, dbs->sector_num, &dbs->iov, dbs->iov.size / 512, dma_blk_cb, dbs); assert(dbs->acb); }
static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBalloon *s = VIRTIO_BALLOON(dev); int ret; virtio_init(vdev, "virtio-balloon", VIRTIO_ID_BALLOON, sizeof(struct virtio_balloon_config)); ret = qemu_add_balloon_handler(virtio_balloon_to_target, virtio_balloon_stat, s); if (ret < 0) { error_setg(errp, "Only one balloon device is supported"); virtio_cleanup(vdev); return; } s->ivq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output); s->dvq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats); if (virtio_has_feature(s->host_features, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) { s->free_page_vq = virtio_add_queue(vdev, VIRTQUEUE_MAX_SIZE, virtio_balloon_handle_free_page_vq); s->free_page_report_status = FREE_PAGE_REPORT_S_STOP; s->free_page_report_cmd_id = VIRTIO_BALLOON_FREE_PAGE_REPORT_CMD_ID_MIN; s->free_page_report_notify.notify = virtio_balloon_free_page_report_notify; precopy_add_notifier(&s->free_page_report_notify); if (s->iothread) { object_ref(OBJECT(s->iothread)); s->free_page_bh = aio_bh_new(iothread_get_aio_context(s->iothread), virtio_ballloon_get_free_page_hints, s); qemu_mutex_init(&s->free_page_lock); qemu_cond_init(&s->free_page_cond); s->block_iothread = false; } else { /* Simply disable this feature if the iothread wasn't created. */ s->host_features &= ~(1 << VIRTIO_BALLOON_F_FREE_PAGE_HINT); virtio_error(vdev, "iothread is missing"); } } reset_stats(s); }
static void blk_alloc(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); Error *err = NULL; trace_xen_disk_alloc(xendev->name); QLIST_INIT(&blkdev->inflight); QLIST_INIT(&blkdev->finished); QLIST_INIT(&blkdev->freelist); blkdev->iothread = iothread_create(xendev->name, &err); assert(!err); blkdev->ctx = iothread_get_aio_context(blkdev->iothread); blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev); }
XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev, BlockConf *conf, IOThread *iothread) { XenBlockDataPlane *dataplane = g_new0(XenBlockDataPlane, 1); dataplane->xendev = xendev; dataplane->blk = conf->blk; QLIST_INIT(&dataplane->inflight); QLIST_INIT(&dataplane->freelist); if (iothread) { dataplane->iothread = iothread; object_ref(OBJECT(dataplane->iothread)); dataplane->ctx = iothread_get_aio_context(dataplane->iothread); } else { dataplane->ctx = qemu_get_aio_context(); } dataplane->bh = aio_bh_new(dataplane->ctx, xen_block_dataplane_bh, dataplane); return dataplane; }
static void blkverify_aio_cb(void *opaque, int ret) { BlkverifyAIOCB *acb = opaque; switch (++acb->done) { case 1: acb->ret = ret; break; case 2: if (acb->ret != ret) { blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); } if (acb->verify) { acb->verify(acb); } acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs), blkverify_aio_bh, acb); qemu_bh_schedule(acb->bh); break; } }
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBlock *s = VIRTIO_BLK(vdev); VirtIOBlockReq *req; MultiReqBuffer mrb = { .num_writes = 0, }; #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start * dataplane here instead of waiting for .set_status(). */ if (s->dataplane) { virtio_blk_data_plane_start(s->dataplane); return; } #endif while ((req = virtio_blk_get_request(s))) { virtio_blk_handle_request(req, &mrb); } virtio_submit_multiwrite(s->bs, &mrb); /* * FIXME: Want to check for completions before returning to guest mode, * so cached reads and writes are reported as quickly as possible. But * that should be done in the generic block layer. */ } static void virtio_blk_dma_restart_bh(void *opaque) { VirtIOBlock *s = opaque; VirtIOBlockReq *req = s->rq; MultiReqBuffer mrb = { .num_writes = 0, }; qemu_bh_delete(s->bh); s->bh = NULL; s->rq = NULL; while (req) { virtio_blk_handle_request(req, &mrb); req = req->next; } virtio_submit_multiwrite(s->bs, &mrb); } static void virtio_blk_dma_restart_cb(void *opaque, int running, RunState state) { VirtIOBlock *s = opaque; if (!running) { return; } if (!s->bh) { s->bh = aio_bh_new(bdrv_get_aio_context(s->blk.conf.bs), virtio_blk_dma_restart_bh, s); qemu_bh_schedule(s->bh); } }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, VirtIOBlockDataPlane **dataplane, Error **errp) { VirtIOBlockDataPlane *s; Error *local_err = NULL; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; if (!conf->data_plane && !conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, "device is incompatible with x-data-plane " "(transport does not support notifiers)"); return; } /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, &local_err)) { error_setg(errp, "cannot start dataplane thread: %s", error_get_pretty(local_err)); error_free(local_err); return; } s = g_new0(VirtIOBlockDataPlane, 1); s->vdev = vdev; s->conf = conf; if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); } else { /* Create per-device IOThread if none specified. This is for * x-data-plane option compatibility. If x-data-plane is removed we * can drop this. */ object_initialize(&s->internal_iothread_obj, sizeof(s->internal_iothread_obj), TYPE_IOTHREAD); user_creatable_complete(OBJECT(&s->internal_iothread_obj), &error_abort); s->iothread = &s->internal_iothread_obj; } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); error_setg(&s->blocker, "block device is in use by data plane"); blk_op_block_all(conf->conf.blk, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); *dataplane = s; }
qemu_mutex_unlock(&data.start_lock); /* let the thread run */ g_assert(aio_poll(ctx, true)); g_assert(!data.thread_acquired); aio_context_release(ctx); qemu_thread_join(&thread); set_event_notifier(ctx, &data.notifier, NULL); event_notifier_cleanup(&data.notifier); g_assert(data.thread_acquired); } static void test_bh_schedule(void) { BHTestData data = { .n = 0 }; data.bh = aio_bh_new(ctx, bh_test_cb, &data); qemu_bh_schedule(data.bh); g_assert_cmpint(data.n, ==, 0); g_assert(aio_poll(ctx, true)); g_assert_cmpint(data.n, ==, 1); g_assert(!aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 1); qemu_bh_delete(data.bh); } static void test_bh_schedule10(void) { BHTestData data = { .n = 0, .max = 10 };
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) { return aio_bh_new(qemu_aio_context, cb, opaque); }
static void handle_notify(EventNotifier *e) { VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane, host_notifier); VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); event_notifier_test_and_clear(&s->host_notifier); blk_io_plug(s->conf->conf.blk); for (;;) { MultiReqBuffer mrb = { .num_writes = 0, }; int ret; /* Disable guest->host notifies to avoid unnecessary vmexits */ vring_disable_notification(s->vdev, &s->vring); for (;;) { VirtIOBlockReq *req = virtio_blk_alloc_request(vblk); ret = vring_pop(s->vdev, &s->vring, &req->elem); if (ret < 0) { virtio_blk_free_request(req); break; /* no more requests */ } trace_virtio_blk_data_plane_process_request(s, req->elem.out_num, req->elem.in_num, req->elem.index); virtio_blk_handle_request(req, &mrb); } virtio_submit_multiwrite(s->conf->conf.blk, &mrb); if (likely(ret == -EAGAIN)) { /* vring emptied */ /* Re-enable guest->host notifies and stop processing the vring. * But if the guest has snuck in more descriptors, keep processing. */ if (vring_enable_notification(s->vdev, &s->vring)) { break; } } else { /* fatal error */ break; } } blk_io_unplug(s->conf->conf.blk); } /* Context: QEMU global mutex held */ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, VirtIOBlockDataPlane **dataplane, Error **errp) { VirtIOBlockDataPlane *s; Error *local_err = NULL; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; if (!conf->data_plane && !conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, "device is incompatible with x-data-plane " "(transport does not support notifiers)"); return; } /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, &local_err)) { error_setg(errp, "cannot start dataplane thread: %s", error_get_pretty(local_err)); error_free(local_err); return; } s = g_new0(VirtIOBlockDataPlane, 1); s->vdev = vdev; s->conf = conf; if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); } else { /* Create per-device IOThread if none specified. This is for * x-data-plane option compatibility. If x-data-plane is removed we * can drop this. */ object_initialize(&s->internal_iothread_obj, sizeof(s->internal_iothread_obj), TYPE_IOTHREAD); user_creatable_complete(OBJECT(&s->internal_iothread_obj), &error_abort); s->iothread = &s->internal_iothread_obj; } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); error_setg(&s->blocker, "block device is in use by data plane"); blk_op_block_all(conf->conf.blk, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); *dataplane = s; }