From nobody Tue Feb 10 20:07:24 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1763782294088932.7290593041653; Fri, 21 Nov 2025 19:31:34 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vMcrW-00069V-3r; Fri, 21 Nov 2025 20:56:31 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vMbpr-0003P8-R0; Fri, 21 Nov 2025 19:50:43 -0500 Received: from isrv.corpit.ru ([212.248.84.144]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vMbph-0000MF-D1; Fri, 21 Nov 2025 19:50:40 -0500 Received: from tsrv.corpit.ru (tsrv.tls.msk.ru [192.168.177.2]) by isrv.corpit.ru (Postfix) with ESMTP id 3177F16CA81; Fri, 21 Nov 2025 21:44:29 +0300 (MSK) Received: from think4mjt.tls.msk.ru (mjtthink.wg.tls.msk.ru [192.168.177.146]) by tsrv.corpit.ru (Postfix) with ESMTP id E3986321CBD; Fri, 21 Nov 2025 21:44:37 +0300 (MSK) From: Michael Tokarev To: qemu-devel@nongnu.org Cc: qemu-stable@nongnu.org, Hanna Czenczek , Kevin Wolf , Michael Tokarev Subject: [Stable-10.0.7 70/81] curl: Fix coroutine waking Date: Fri, 21 Nov 2025 21:44:09 +0300 Message-ID: <20251121184424.1137669-70-mjt@tls.msk.ru> X-Mailer: git-send-email 2.47.3 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=212.248.84.144; envelope-from=mjt@tls.msk.ru; helo=isrv.corpit.ru X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1763782295788018900 From: Hanna Czenczek If we wake a coroutine from a different context, we must ensure that it will yield exactly once (now or later), awaiting that wake. curl=E2=80=99s current .ret =3D=3D -EINPROGRESS loop may lead to the corout= ine not yielding if the request finishes before the loop gets run. To fix it, we must drop the loop and yield exactly once, if we need to yield. Finding out that latter part ("if we need to yield") makes it a bit complicated: Requests may be served from a cache internal to the curl block driver, or fail before being submitted. In these cases, we must not yield. However, if we find a matching but still ongoing request in the cache, we will have to await that, i.e. still yield. To address this, move the yield inside of the respective functions: - Inside of curl_find_buf() when awaiting ongoing concurrent requests, - Inside of curl_setup_preadv() when having created a new request. Rename curl_setup_preadv() to curl_do_preadv() to reflect this. (Can be reproduced with multiqueue by adding a usleep(100000) before the `while (acb.ret =3D=3D -EINPROGRESS)` loop.) Also, add a comment why aio_co_wake() is safe regardless of whether the coroutine and curl_multi_check_completion() run in the same context. Cc: qemu-stable@nongnu.org Signed-off-by: Hanna Czenczek Message-ID: <20251110154854.151484-6-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf (cherry picked from commit 53d5c7ffac7bd4e0d12174432ebb2b3e88614b15) Signed-off-by: Michael Tokarev diff --git a/block/curl.c b/block/curl.c index cbfb847dc4..96498aac1d 100644 --- a/block/curl.c +++ b/block/curl.c @@ -258,8 +258,8 @@ read_end: } =20 /* 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 *a= cb) { int i; uint64_t end =3D start + len; @@ -307,6 +307,10 @@ static bool curl_find_buf(BDRVCURLState *s, uint64_t s= tart, uint64_t len, for (j=3D0; jacb[j]) { state->acb[j] =3D acb; + /* Await ongoing request */ + qemu_mutex_unlock(&s->mutex); + qemu_coroutine_yield(); + qemu_mutex_lock(&s->mutex); return true; } } @@ -378,6 +382,16 @@ static void curl_multi_check_completion(BDRVCURLState = *s) acb->ret =3D error ? -EIO : 0; state->acb[i] =3D 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 h= andler + * (curl_multi_do()/curl_multi_timeout_do()) could not h= ave + * 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 yie= ld + * So aio_co_wake() is safe to call. + */ aio_co_wake(acb->co); qemu_mutex_lock(&s->mutex); } @@ -879,7 +893,7 @@ out_noclean: return -EINVAL; } =20 -static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB= *acb) +static void coroutine_fn curl_do_preadv(BlockDriverState *bs, CURLAIOCB *a= cb) { CURLState *state; int running; @@ -891,10 +905,13 @@ static void coroutine_fn curl_setup_preadv(BlockDrive= rState *bs, CURLAIOCB *acb) =20 qemu_mutex_lock(&s->mutex); =20 - // 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; } =20 // No cache found, so let's start a new request @@ -909,7 +926,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverS= tate *bs, CURLAIOCB *acb) if (curl_init_state(s, state) < 0) { curl_clean_state(state); acb->ret =3D -EIO; - goto out; + goto dont_yield; } =20 acb->start =3D 0; @@ -924,7 +941,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverS= tate *bs, CURLAIOCB *acb) if (state->buf_len && state->orig_buf =3D=3D NULL) { curl_clean_state(state); acb->ret =3D -ENOMEM; - goto out; + goto dont_yield; } state->acb[0] =3D acb; =20 @@ -936,13 +953,16 @@ static void coroutine_fn curl_setup_preadv(BlockDrive= rState *bs, CURLAIOCB *acb) acb->ret =3D -EIO; =20 curl_clean_state(state); - goto out; + goto dont_yield; } =20 /* 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; =20 -out: +dont_yield: qemu_mutex_unlock(&s->mutex); } =20 @@ -958,10 +978,7 @@ static int coroutine_fn curl_co_preadv(BlockDriverStat= e *bs, .bytes =3D bytes }; =20 - curl_setup_preadv(bs, &acb); - while (acb.ret =3D=3D -EINPROGRESS) { - qemu_coroutine_yield(); - } + curl_do_preadv(bs, &acb); return acb.ret; } =20 --=20 2.47.3