}
/* Called with s->mutex held. */
-static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
- CURLAIOCB *acb)
+static bool coroutine_fn
+curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, CURLAIOCB *acb)
{
int i;
uint64_t end = start + len;
for (j=0; j<CURL_NUM_ACB; j++) {
if (!state->acb[j]) {
state->acb[j] = acb;
+ /* Await ongoing request */
+ qemu_mutex_unlock(&s->mutex);
+ qemu_coroutine_yield();
+ qemu_mutex_lock(&s->mutex);
return true;
}
}
acb->ret = error ? -EIO : 0;
state->acb[i] = NULL;
qemu_mutex_unlock(&s->mutex);
+ /*
+ * Current AioContext is the BDS context, which may or may not
+ * be the request (coroutine) context.
+ * - If it is, the coroutine must have yielded or the FD handler
+ * (curl_multi_do()/curl_multi_timeout_do()) could not have
+ * been called and we would not be here
+ * - If it is not, it doesn't matter whether it has already
+ * yielded or not; it will be scheduled once it does yield
+ * So aio_co_wake() is safe to call.
+ */
aio_co_wake(acb->co);
qemu_mutex_lock(&s->mutex);
}
return -EINVAL;
}
-static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
+static void coroutine_fn curl_do_preadv(BlockDriverState *bs, CURLAIOCB *acb)
{
CURLState *state;
int running;
qemu_mutex_lock(&s->mutex);
- // In case we have the requested data already (e.g. read-ahead),
- // we can just call the callback and be done.
+ /*
+ * In case we have the requested data already (e.g. read-ahead),
+ * we can just call the callback and be done. This may have to
+ * await an ongoing request, in which case it itself will yield.
+ */
if (curl_find_buf(s, start, acb->bytes, acb)) {
- goto out;
+ goto dont_yield;
}
// No cache found, so let's start a new request
if (curl_init_state(s, state) < 0) {
curl_clean_state(state);
acb->ret = -EIO;
- goto out;
+ goto dont_yield;
}
acb->start = 0;
if (state->buf_len && state->orig_buf == NULL) {
curl_clean_state(state);
acb->ret = -ENOMEM;
- goto out;
+ goto dont_yield;
}
state->acb[0] = acb;
acb->ret = -EIO;
curl_clean_state(state);
- goto out;
+ goto dont_yield;
}
/* Tell curl it needs to kick things off */
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
+ qemu_mutex_unlock(&s->mutex);
+ qemu_coroutine_yield();
+ return;
-out:
+dont_yield:
qemu_mutex_unlock(&s->mutex);
}
.bytes = bytes
};
- curl_setup_preadv(bs, &acb);
- while (acb.ret == -EINPROGRESS) {
- qemu_coroutine_yield();
- }
+ curl_do_preadv(bs, &acb);
return acb.ret;
}