The curl block driver uses fd handlers, timers, and BHs. The fd handlers and timers are managed on behalf of libcurl, which controls them using callback functions that the block driver implements.
The simplest way to implement .bdrv_detach/attach_aio_context() is to clean up libcurl in the old event loop and initialize it again in the new event loop. We do not need to keep track of anything since there are no pending requests when the AioContext is changed. Also make sure to use aio_set_fd_handler() instead of qemu_aio_set_fd_handler() and aio_bh_new() instead of qemu_bh_new() so the current AioContext is passed in. Cc: Alexander Graf <ag...@suse.de> Cc: Fam Zheng <f...@redhat.com> Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com> --- block/curl.c | 194 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 116 insertions(+), 78 deletions(-) diff --git a/block/curl.c b/block/curl.c index 6731d28..88638ec 100644 --- a/block/curl.c +++ b/block/curl.c @@ -88,6 +88,7 @@ typedef struct BDRVCURLState { char *url; size_t readahead_size; bool accept_range; + AioContext *aio_context; } BDRVCURLState; static void curl_clean_state(CURLState *s); @@ -111,21 +112,24 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) #endif static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, - void *s, void *sp) + void *userp, void *sp) { + BDRVCURLState *s = userp; + DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); switch (action) { case CURL_POLL_IN: - qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, s); + aio_set_fd_handler(s->aio_context, fd, curl_multi_do, NULL, s); break; case CURL_POLL_OUT: - qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, s); + aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, s); break; case CURL_POLL_INOUT: - qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do, s); + aio_set_fd_handler(s->aio_context, fd, curl_multi_do, + curl_multi_do, s); break; case CURL_POLL_REMOVE: - qemu_aio_set_fd_handler(fd, NULL, NULL, NULL); + aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL); break; } @@ -430,6 +434,55 @@ static void curl_parse_filename(const char *filename, QDict *options, g_free(file); } +static void curl_detach_aio_context(BlockDriverState *bs) +{ + BDRVCURLState *s = bs->opaque; + int i; + + for (i = 0; i < CURL_NUM_STATES; i++) { + if (s->states[i].in_use) { + curl_clean_state(&s->states[i]); + } + if (s->states[i].curl) { + curl_easy_cleanup(s->states[i].curl); + s->states[i].curl = NULL; + } + if (s->states[i].orig_buf) { + g_free(s->states[i].orig_buf); + s->states[i].orig_buf = NULL; + } + } + if (s->multi) { + curl_multi_cleanup(s->multi); + s->multi = NULL; + } + + timer_del(&s->timer); +} + +static void curl_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVCURLState *s = bs->opaque; + + aio_timer_init(new_context, &s->timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + curl_multi_timeout_do, s); + + // Now we know the file exists and its size, so let's + // initialize the multi interface! + + s->multi = curl_multi_init(); + s->aio_context = new_context; + curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s); + curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); +#ifdef NEED_CURL_TIMER_CALLBACK + curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); + curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); +#endif + curl_multi_do(s); +} + static QemuOptsList runtime_opts = { .name = "curl", .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), @@ -523,21 +576,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, curl_easy_cleanup(state->curl); state->curl = NULL; - aio_timer_init(bdrv_get_aio_context(bs), &s->timer, - QEMU_CLOCK_REALTIME, SCALE_NS, - curl_multi_timeout_do, s); - - // Now we know the file exists and its size, so let's - // initialize the multi interface! - - s->multi = curl_multi_init(); - curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s); - curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); -#ifdef NEED_CURL_TIMER_CALLBACK - curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); - curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); -#endif - curl_multi_do(s); + curl_attach_aio_context(bs, bdrv_get_aio_context(bs)); qemu_opts_del(opts); return 0; @@ -630,7 +669,7 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, acb->sector_num = sector_num; acb->nb_sectors = nb_sectors; - acb->bh = qemu_bh_new(curl_readv_bh_cb, acb); + acb->bh = aio_bh_new(bdrv_get_aio_context(bs), curl_readv_bh_cb, acb); qemu_bh_schedule(acb->bh); return &acb->common; } @@ -638,25 +677,9 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, static void curl_close(BlockDriverState *bs) { BDRVCURLState *s = bs->opaque; - int i; DPRINTF("CURL: Close\n"); - for (i=0; i<CURL_NUM_STATES; i++) { - if (s->states[i].in_use) - curl_clean_state(&s->states[i]); - if (s->states[i].curl) { - curl_easy_cleanup(s->states[i].curl); - s->states[i].curl = NULL; - } - if (s->states[i].orig_buf) { - g_free(s->states[i].orig_buf); - s->states[i].orig_buf = NULL; - } - } - if (s->multi) - curl_multi_cleanup(s->multi); - - timer_del(&s->timer); + curl_detach_aio_context(bs); g_free(s->url); } @@ -668,68 +691,83 @@ static int64_t curl_getlength(BlockDriverState *bs) } static BlockDriver bdrv_http = { - .format_name = "http", - .protocol_name = "http", + .format_name = "http", + .protocol_name = "http", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, - .instance_size = sizeof(BDRVCURLState), - .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, - .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_readv = curl_aio_readv, + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, }; static BlockDriver bdrv_https = { - .format_name = "https", - .protocol_name = "https", + .format_name = "https", + .protocol_name = "https", - .instance_size = sizeof(BDRVCURLState), - .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, - .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, - .bdrv_aio_readv = curl_aio_readv, + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, }; static BlockDriver bdrv_ftp = { - .format_name = "ftp", - .protocol_name = "ftp", + .format_name = "ftp", + .protocol_name = "ftp", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, - .instance_size = sizeof(BDRVCURLState), - .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, - .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_readv = curl_aio_readv, + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, }; static BlockDriver bdrv_ftps = { - .format_name = "ftps", - .protocol_name = "ftps", + .format_name = "ftps", + .protocol_name = "ftps", - .instance_size = sizeof(BDRVCURLState), - .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, - .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, - .bdrv_aio_readv = curl_aio_readv, + .bdrv_aio_readv = curl_aio_readv, + + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, }; static BlockDriver bdrv_tftp = { - .format_name = "tftp", - .protocol_name = "tftp", + .format_name = "tftp", + .protocol_name = "tftp", + + .instance_size = sizeof(BDRVCURLState), + .bdrv_parse_filename = curl_parse_filename, + .bdrv_file_open = curl_open, + .bdrv_close = curl_close, + .bdrv_getlength = curl_getlength, - .instance_size = sizeof(BDRVCURLState), - .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, - .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_readv = curl_aio_readv, + .bdrv_detach_aio_context = curl_detach_aio_context, + .bdrv_attach_aio_context = curl_attach_aio_context, }; static void curl_block_init(void) -- 1.9.0