From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160081; cv=none; d=zohomail.com; s=zohoarc; b=OHp3SokaiWvpcBi+VS7stciUYylvqOu/q8MQDLUtZsZ5ytBXsecRKISiTejLJRCoGinELnUTKcHwykPyy49nIemkn2ZGeemhoaHR0tv6V9Mrm4e23KSqLyqEMAyfz+B1m6y9hxf8bGTOVUNKL5OC9xExluqMl9d1x6YRfJfFcZk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160081; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=RQkpAxemdsSNz/LwAsMYWb7MqtumwB5V5YwGFJBCEjs=; b=nVwbHBapjpKHKmMDAVh91YpwGuK6gosmwH8f3HZx9CFeTUbOKYVMa3H86KGPkcj7ZaDe7NC2O7Fq/PQO2thon3CeAYUYhPaP5RdSGeHFjoYWcr3uXUwAFHJJl+NLmdPCrPlcC4+PNZihygNDSUyDyiIyVugLrA84gq8iKrNUJtA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177316008156856.00813425671288; Tue, 10 Mar 2026 09:28:01 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv7-0003Jk-RU; Tue, 10 Mar 2026 12:26:57 -0400 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 1vzzur-0003Dl-8P for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:45 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzuo-0000nv-AZ for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:40 -0400 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-463-DlLSiRyVOV6GMuziqKv9Gw-1; Tue, 10 Mar 2026 12:26:33 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C73CE19560AF; Tue, 10 Mar 2026 16:26:31 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A0D3C19560B7; Tue, 10 Mar 2026 16:26:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773159997; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RQkpAxemdsSNz/LwAsMYWb7MqtumwB5V5YwGFJBCEjs=; b=HHjConBzw+z8yH6afEO2nB1Ia/F5xf/DLN1tY3C/EogVkFZltFDUREDT8quacrNP5ZJd50 wnlf2DvRi/R0RIHhJo2xyu1sM6CUjht5vjB7mlUFw5FVJHGLJL+gBpviqxf27wOkZYtZld J/dbcY9GiJnET70c6vVnNEX+n1OgYnY= X-MC-Unique: DlLSiRyVOV6GMuziqKv9Gw-1 X-Mimecast-MFC-AGG-ID: DlLSiRyVOV6GMuziqKv9Gw_1773159992 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 01/28] fuse: Copy write buffer content before polling Date: Tue, 10 Mar 2026 17:25:55 +0100 Message-ID: <20260310162622.333137-2-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160082892158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek aio_poll() in I/O functions can lead to nested read_from_fuse_export() calls, overwriting the request buffer's content. The only function affected by this is fuse_write(), which therefore must use a bounce buffer or corruption may occur. Note that in addition we do not know whether libfuse-internal structures can cope with this nesting, and even if we did, we probably cannot rely on it in the future. This is the main reason why we want to remove libfuse from the I/O path. I do not have a good reproducer for this other than: $ dd if=3D/dev/urandom of=3Dimage bs=3D1M count=3D4096 $ dd if=3D/dev/zero of=3Dcopy bs=3D1M count=3D4096 $ touch fuse-export $ qemu-storage-daemon \ --blockdev file,node-name=3Dfile,filename=3Dcopy \ --export \ fuse,id=3Dexp,node-name=3Dfile,mountpoint=3Dfuse-export,writable=3Dtrue= \ & Other shell: $ qemu-img convert -p -n -f raw -O raw -t none image fuse-export $ killall -SIGINT qemu-storage-daemon $ qemu-img compare image copy Content mismatch at offset 0! (The -t none in qemu-img convert is important.) I tried reproducing this with throttle and small aio_write requests from another qemu-io instance, but for some reason all requests are perfectly serialized then. I think in theory we should get parallel writes only if we set fi->parallel_direct_writes in fuse_open(). In fact, I can confirm that if we do that, that throttle-based reproducer works (i.e. does get parallel (nested) write requests). I have no idea why we still get parallel requests with qemu-img convert anyway. Also, a later patch in this series will set fi->parallel_direct_writes and note that it makes basically no difference when running fio on the current libfuse-based version of our code. It does make a difference without libfuse. So something quite fishy is going on. I will try to investigate further what the root cause is, but I think for now let's assume that calling blk_pwrite() can invalidate the buffer contents through nested polling. Cc: qemu-stable@nongnu.org Reviewed-by: Kevin Wolf Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-2-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 8cf4572f78d..cea9de61f1d 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -301,6 +301,12 @@ static void read_from_fuse_export(void *opaque) goto out; } =20 + /* + * Note that aio_poll() in any request-processing function can lead to= a + * nested read_from_fuse_export() call, which will overwrite the conte= nts of + * exp->fuse_buf. Anything that takes a buffer needs to take care tha= t the + * content is copied before potentially polling via aio_poll(). + */ fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); =20 out: @@ -624,6 +630,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode= , const char *buf, size_t size, off_t offset, struct fuse_file_info *f= i) { FuseExport *exp =3D fuse_req_userdata(req); + QEMU_AUTO_VFREE void *copied =3D NULL; int64_t length; int ret; =20 @@ -638,6 +645,14 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inod= e, const char *buf, return; } =20 + /* + * Heed the note on read_from_fuse_export(): If we call aio_poll() (wh= ich + * any blk_*() I/O function may do), read_from_fuse_export() may be ne= sted, + * overwriting the request buffer content. Therefore, we must copy it= here. + */ + copied =3D blk_blockalign(exp->common.blk, size); + memcpy(copied, buf, size); + /** * Clients will expect short writes at EOF, so we have to limit * offset+size to the image length. @@ -660,7 +675,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode= , const char *buf, } } =20 - ret =3D blk_pwrite(exp->common.blk, offset, size, buf, 0); + ret =3D blk_pwrite(exp->common.blk, offset, size, copied, 0); if (ret >=3D 0) { fuse_reply_write(req, size); } else { --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160037; cv=none; d=zohomail.com; s=zohoarc; b=ktD8sPHBtUW3IrLSWjVZSW7vc+toIbGu9wqEPxr5PNXPo8XouHD0APOJZq2wFTJz+2JneYxGyqRAwDRhwUQU6hV6B/8wN5WTW38TyL9MQ0AqYRy1Tky6kKWus4sWj3NIeOMap8hBm9tu2Msk+OlWUA9ztoVX7GYlFekXbM2tRWI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160037; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=NRhrwq6RsqhB+VfcXaCP0/Mi2Y5rzGbQxoIrTXdEGhA=; b=CVRZpAFXTFBB2bCefI7h8m2pjaHHkpKNG0a5ZhEORWaPycpvL9+mlmUf/ruczhTe7m4FJ3HwuhwvScs4hXasCXUWBIjA9/0YEOLeDM3sMeEIgTPBcKs7SPjGCxUZLgv6V0HU7g4jXF8dt9gDjGL2POrWOBmuVH+dqzExHDnpccw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177316003755148.76938782331683; Tue, 10 Mar 2026 09:27:17 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv3-0003G5-TC; Tue, 10 Mar 2026 12:26:55 -0400 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 1vzzus-0003Dq-L0 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:45 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzur-0000o2-8V for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:42 -0400 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-261-GyXUpRueObKoTZnsE0U_9Q-1; Tue, 10 Mar 2026 12:26:34 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 63FCA1955F25; Tue, 10 Mar 2026 16:26:33 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3C02219560A6; Tue, 10 Mar 2026 16:26:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773159998; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NRhrwq6RsqhB+VfcXaCP0/Mi2Y5rzGbQxoIrTXdEGhA=; b=CxVzrqCSOlukt3DnXZ2ynpuI5+ELJxczyK0a0pvvoIR9sp2fUxODjkLNd7O7VbMLFdFGO9 OXgrr3VdhV1UITRCowwXrylHtri8Gzb+GEAzgcHvfkDo9XQ0h3xgkUiwqD5Okgb8WZ5LFv IV1ET8o+v4rzi/Ceu1LEnyH9n86Q82Y= X-MC-Unique: GyXUpRueObKoTZnsE0U_9Q-1 X-Mimecast-MFC-AGG-ID: GyXUpRueObKoTZnsE0U_9Q_1773159993 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 02/28] fuse: Ensure init clean-up even with error_fatal Date: Tue, 10 Mar 2026 17:25:56 +0100 Message-ID: <20260310162622.333137-3-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160039032158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek When exports are created on the command line (with the storage daemon), errp is going to point to error_fatal. Without ERRP_GUARD, we would exit immediately when *errp is set, i.e. skip the clean-up code under the `fail` label. Use ERRP_GUARD so we always run that code. As far as I know, this has no actual impact right now[1], but it is still better to make this right. [1] Not cleaning up the mount point is the only thing I can imagine would be problematic, but that is the last thing we attempt, so if it fails, it will clean itself up. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-3-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/export/fuse.c b/block/export/fuse.c index cea9de61f1d..2ed22c6b2f5 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -119,6 +119,7 @@ static int fuse_export_create(BlockExport *blk_exp, BlockExportOptions *blk_exp_args, Error **errp) { + ERRP_GUARD(); /* ensure clean-up even with error_fatal */ FuseExport *exp =3D container_of(blk_exp, FuseExport, common); BlockExportOptionsFuse *args =3D &blk_exp_args->u.fuse; int ret; --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160044; cv=none; d=zohomail.com; s=zohoarc; b=aSYzLD8ApROMDs7fNqj2quJjkKEsPQeqTFh79CC4PO5isdCI+acYLp91ieuKQcReJ8lNq8/s86c52egD0bSfyiGa1NPeyqhHcf8bQ4a20mPtspIRxuASPs7h8Tx5KJARjHO3HDt+OgHiZnUYA2HV5HrKrfg5iQWWTJwYxLAJJS8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160044; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=uuDeIyuIZlz0JoAO96xZZ7pGGObXXGj9N0zDGuRj/t8=; b=c5Dtyth91D3vL2VREmC6P92NmNIngmBk/iK92JZqRqDaqzB1ITJ0YeqVAVaT2O+eXQUhVerDlmq7/5ZwdH7FwMEATQ/f7uYLpW2P2DY97VFYoFW0JXv/zmB2fbEbfSGt7nDcswXjHv4UIzOLL2gWztXrxg+Vy3tssX9AYGRJIGY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160044487435.39690994284365; Tue, 10 Mar 2026 09:27:24 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvB-0003Mb-Hh; Tue, 10 Mar 2026 12:27:01 -0400 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 1vzzur-0003Dm-9K for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:45 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzuo-0000nx-DV for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:40 -0400 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-449-sSNKf5e9Pli9dfxBvz2aOQ-1; Tue, 10 Mar 2026 12:26:36 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 6ABD119560BE; Tue, 10 Mar 2026 16:26:35 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1D09519560B7; Tue, 10 Mar 2026 16:26:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773159997; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uuDeIyuIZlz0JoAO96xZZ7pGGObXXGj9N0zDGuRj/t8=; b=W9r0KkXubpuOZYxMu+biUgOuTNfZgEmy4kPru7/Oh2eK8OFvDNOruXFEW3vl433D1J+XcE gfUK+zSsQkgzRcCkr9tOqHJ83FgjKj4JBJFlcHvY+UPF2+IXuPXiw/7qKS+rQMaMMiWhtB cF37p95rBH4V7LRXrHHMA2N7hItU2JU= X-MC-Unique: sSNKf5e9Pli9dfxBvz2aOQ-1 X-Mimecast-MFC-AGG-ID: sSNKf5e9Pli9dfxBvz2aOQ_1773159995 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 03/28] fuse: Remove superfluous empty line Date: Tue, 10 Mar 2026 17:25:57 +0100 Message-ID: <20260310162622.333137-4-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160046511158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-4-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 2ed22c6b2f5..4cdf527d691 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -464,7 +464,6 @@ static int fuse_do_truncate(const FuseExport *exp, int6= 4_t size, } =20 if (add_resize_perm) { - if (!qemu_in_main_thread()) { /* Changing permissions like below only works in the main thre= ad */ return -EPERM; --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160114; cv=none; d=zohomail.com; s=zohoarc; b=CfveCsQlpXaRlfShbVx39CiSnlRqliGFsmK3wW/WL17J0vEWatSoLy0Tsl0+vK3V9aoNs4HZok9WgzUcpmh4ys2O0HvJgVEp99x6lXbm/ZDPYjCTWMr0eT4mBhq1rgU6DB2S0Z4JxQU6bFs8+rvR46h4snaZ/qNLE2HH79YBDns= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160114; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=mwjI0Uoikybiyvq3LbfzcY1cPkrDRh/8DeKy6dcouYo=; b=P4wHNyajEZ9lg7Ov7fvifOPlTJ+AddjVnUItTiF79x0qlwwzZWGZy5Iw1qU10iAWJtYYmQLzWEkv3q+eR/ywIqmqgsUfe71GAADMkBxvzZhXZrDwURaUb0EDdi0PujeZ5MZ6OOQRPUw4LAkSb3lbr8wik1nSuJiyXzvC6npH7B4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17731601141201011.6199705020699; Tue, 10 Mar 2026 09:28:34 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv6-0003Hu-HK; Tue, 10 Mar 2026 12:26:56 -0400 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 1vzzut-0003Dr-HI for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:45 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzur-0000oH-EB for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:42 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-312-qQUw9MMaN2O89tWlIn2fmw-1; Tue, 10 Mar 2026 12:26:37 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 106D8180044D; Tue, 10 Mar 2026 16:26:37 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D4B8F1955F2D; Tue, 10 Mar 2026 16:26:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773159999; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mwjI0Uoikybiyvq3LbfzcY1cPkrDRh/8DeKy6dcouYo=; b=GU5HFZs1LBk7YVUDvoomIubN5vOim35r5GQrY2uhYRgNknnxY5yntokGxLEAxN+4g8GY88 KWr20suI5OrgOmLvaYepybU1yb1/bTqKFWlZaB+7zHnL9Xzt1EMVFVIhMxHgFRGvm5z7Cy KpVKE4sHyPzcMNvJ1W3sqwnOum+jKRc= X-MC-Unique: qQUw9MMaN2O89tWlIn2fmw-1 X-Mimecast-MFC-AGG-ID: qQUw9MMaN2O89tWlIn2fmw_1773159997 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 04/28] fuse: Explicitly set inode ID to 1 Date: Tue, 10 Mar 2026 17:25:58 +0100 Message-ID: <20260310162622.333137-5-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160115317154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Setting .st_ino to the FUSE inode ID is kind of arbitrary. While in practice it is going to be fixed (to FUSE_ROOT_ID, which is 1) because we only have the root inode, that is not obvious in fuse_getattr(). Just explicitly set it to 1 (i.e. no functional change). Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-5-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 4cdf527d691..a56f645c05d 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -432,7 +432,7 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t ino= de, } =20 statbuf =3D (struct stat) { - .st_ino =3D inode, + .st_ino =3D 1, .st_mode =3D exp->st_mode, .st_nlink =3D 1, .st_uid =3D exp->st_uid, --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160057; cv=none; d=zohomail.com; s=zohoarc; b=Xn78BVtUL0yChCJ7MYtaT61qajvr6mC7qpNFEeWzztEUdLGBc18mAiMDRxJsX/r4u7qWoZjDnesariqy4JtrkXjHjUUasmS87Re/JMocltechtuEx54D8HMk2O6ZnQwXbWodwL6qzDQWFDCJAIWS1dDJhmy3N1hTBSufqZF4598= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160057; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=SEhyHYVkrtozPczJJn4tjxRJRMbKxsS3I+NZXoKZ698=; b=MV/1xOIijFseoxe6TjTTMSXXulxR1uy+b3+ZyobZ0jI5y6D9R8PKoGM43wrhYlI9FMd7ovJLdBGF+j387iysGAyK1VclO+gs5YVic5a+DmlUg0yZ6J36pFw63qRxc1fHDV/lesrBIhiLe6t/TPM9T3faz7cCOm6ljLk5XGjpivA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17731600577087.4839945530746945; Tue, 10 Mar 2026 09:27:37 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvB-0003Mv-Tq; Tue, 10 Mar 2026 12:27:02 -0400 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 1vzzuv-0003E3-Um for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:46 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzuu-0000pm-F5 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:45 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-613-7NyNzwkbPq6E-fB2Wsg1HQ-1; Tue, 10 Mar 2026 12:26:39 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AE7F11955E70; Tue, 10 Mar 2026 16:26:38 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7A79419560A6; Tue, 10 Mar 2026 16:26:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160003; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SEhyHYVkrtozPczJJn4tjxRJRMbKxsS3I+NZXoKZ698=; b=R6ASNA2Sammod5B139FL0dxKLQY6N/lSuMdzIaV0lxaJavEgeFPG0ZpppomsCbHYFzLfYf CwZrUtElDO9gP4cpsi1NydWQ6lBm8cK21Y/lvPMSmW7W2HnGyZn5c+AGzIYUgtPhplm/dP JCXbj6FYlHGOE4X2YPMd+lhxU9t5048= X-MC-Unique: 7NyNzwkbPq6E-fB2Wsg1HQ-1 X-Mimecast-MFC-AGG-ID: 7NyNzwkbPq6E-fB2Wsg1HQ_1773159998 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 05/28] fuse: Change setup_... to mount_fuse_export() Date: Tue, 10 Mar 2026 17:25:59 +0100 Message-ID: <20260310162622.333137-6-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160060536154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek There is no clear separation between what should go into setup_fuse_export() and what should stay in fuse_export_create(). Make it clear that setup_fuse_export() is for mounting only. Rename it, and move everything that has nothing to do with mounting up into fuse_export_create(). Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-6-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 49 ++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index a56f645c05d..00bb2ffee40 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -72,8 +72,7 @@ static void fuse_export_delete(BlockExport *exp); =20 static void init_exports_table(void); =20 -static int setup_fuse_export(FuseExport *exp, const char *mountpoint, - bool allow_other, Error **errp); +static int mount_fuse_export(FuseExport *exp, Error **errp); static void read_from_fuse_export(void *opaque); =20 static bool is_regular_file(const char *path, Error **errp); @@ -193,23 +192,32 @@ static int fuse_export_create(BlockExport *blk_exp, exp->st_gid =3D getgid(); =20 if (args->allow_other =3D=3D FUSE_EXPORT_ALLOW_OTHER_AUTO) { - /* Ignore errors on our first attempt */ - ret =3D setup_fuse_export(exp, args->mountpoint, true, NULL); - exp->allow_other =3D ret =3D=3D 0; + /* Try allow_other =3D=3D true first, ignore errors */ + exp->allow_other =3D true; + ret =3D mount_fuse_export(exp, NULL); if (ret < 0) { - ret =3D setup_fuse_export(exp, args->mountpoint, false, errp); + exp->allow_other =3D false; + ret =3D mount_fuse_export(exp, errp); } } else { exp->allow_other =3D args->allow_other =3D=3D FUSE_EXPORT_ALLOW_OT= HER_ON; - ret =3D setup_fuse_export(exp, args->mountpoint, exp->allow_other,= errp); + ret =3D mount_fuse_export(exp, errp); } if (ret < 0) { goto fail; } =20 + g_hash_table_insert(exports, g_strdup(exp->mountpoint), NULL); + + aio_set_fd_handler(exp->common.ctx, + fuse_session_fd(exp->fuse_session), + read_from_fuse_export, NULL, NULL, NULL, exp); + exp->fd_handler_set_up =3D true; + return 0; =20 fail: + fuse_export_shutdown(blk_exp); fuse_export_delete(blk_exp); return ret; } @@ -227,10 +235,10 @@ static void init_exports_table(void) } =20 /** - * Create exp->fuse_session and mount it. + * Create exp->fuse_session and mount it. Expects exp->mountpoint, + * exp->writable, and exp->allow_other to be set as intended for the mount. */ -static int setup_fuse_export(FuseExport *exp, const char *mountpoint, - bool allow_other, Error **errp) +static int mount_fuse_export(FuseExport *exp, Error **errp) { const char *fuse_argv[4]; char *mount_opts; @@ -243,7 +251,7 @@ static int setup_fuse_export(FuseExport *exp, const cha= r *mountpoint, */ mount_opts =3D g_strdup_printf("max_read=3D%zu,default_permissions%s", FUSE_MAX_BOUNCE_BYTES, - allow_other ? ",allow_other" : ""); + exp->allow_other ? ",allow_other" : ""); =20 fuse_argv[0] =3D ""; /* Dummy program name */ fuse_argv[1] =3D "-o"; @@ -256,30 +264,17 @@ static int setup_fuse_export(FuseExport *exp, const c= har *mountpoint, g_free(mount_opts); if (!exp->fuse_session) { error_setg(errp, "Failed to set up FUSE session"); - ret =3D -EIO; - goto fail; + return -EIO; } =20 - ret =3D fuse_session_mount(exp->fuse_session, mountpoint); + ret =3D fuse_session_mount(exp->fuse_session, exp->mountpoint); if (ret < 0) { error_setg(errp, "Failed to mount FUSE session to export"); - ret =3D -EIO; - goto fail; + return -EIO; } exp->mounted =3D true; =20 - g_hash_table_insert(exports, g_strdup(mountpoint), NULL); - - aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), - read_from_fuse_export, NULL, NULL, NULL, exp); - exp->fd_handler_set_up =3D true; - return 0; - -fail: - fuse_export_shutdown(&exp->common); - return ret; } =20 /** --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160037; cv=none; d=zohomail.com; s=zohoarc; b=V+UmnaXzj/pB6KzGTSqlFayVQSxxGoVkDV8VUM0l+ihTqF3z7wqKomvKwfHht+lFmTE+JBA6AuIUpCUcWAX2iyG//tQAye22pub0A+SdN2jVaYmYwcXx/Q29lR+a2hsBAtdN5D2C/8W3qBtvDcLUXrkl9GQnJqNvYpqp85HI708= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160037; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=ITSClxvGZk1A1oF1WXDat8zlVUrbDUJk1SSWwGmQKMY=; b=fdAP9HOsZMQDTIKtXrjTtT0uk9MOVaxRUAJN/j3MPn/AFKqo6HCHlqIDMWtJU2vdpL/MM4lWzQT7fqfoVCk0ZtDe82m8T6boa8zi9CJx4uJ+n2CIjAhjPNOes/IpMTd3R42I1ZYRKN7c1gAki4b2UEUPOK+uRgmUJTF5QBKrhfM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160037562416.716792951368; Tue, 10 Mar 2026 09:27:17 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv6-0003Iw-Sq; Tue, 10 Mar 2026 12:26:56 -0400 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 1vzzux-0003FH-Ai for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:48 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzuv-0000px-Cd for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:46 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-445-YSO3q9NlM7edrcuHBK4GnA-1; Tue, 10 Mar 2026 12:26:41 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 53AAF180044D; Tue, 10 Mar 2026 16:26:40 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2490019560A6; Tue, 10 Mar 2026 16:26:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160004; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ITSClxvGZk1A1oF1WXDat8zlVUrbDUJk1SSWwGmQKMY=; b=F+Ekyuj/zITjk+7Gh9j0rCvlpZp2mg3fclgFQAImyprW+dTM9IraNgOEb+eo4B6MOWMq8C hU9CyAoOU+/9d63va9YKH5X/HAZ/NHzSxtpn6hC+m5DZkoMYagEC7RuZ0pgyjrokwz5+Ap hEGMs6x7wPzGfQPVFI82pS108LjQ8n0= X-MC-Unique: YSO3q9NlM7edrcuHBK4GnA-1 X-Mimecast-MFC-AGG-ID: YSO3q9NlM7edrcuHBK4GnA_1773160000 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 06/28] fuse: Destroy session on mount_fuse_export() fail Date: Tue, 10 Mar 2026 17:26:00 +0100 Message-ID: <20260310162622.333137-7-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160038882158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek If mount_fuse_export() fails to mount the session, destroy it. Depending on the allow_other configuration, fuse_export_create() may retry this function on error, which may leak one session instance otherwise. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-7-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 00bb2ffee40..82560ca071f 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -270,11 +270,17 @@ static int mount_fuse_export(FuseExport *exp, Error *= *errp) ret =3D fuse_session_mount(exp->fuse_session, exp->mountpoint); if (ret < 0) { error_setg(errp, "Failed to mount FUSE session to export"); - return -EIO; + ret =3D -EIO; + goto fail; } exp->mounted =3D true; =20 return 0; + +fail: + fuse_session_destroy(exp->fuse_session); + exp->fuse_session =3D NULL; + return ret; } =20 /** --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160212; cv=none; d=zohomail.com; s=zohoarc; b=LsZzCwXvmrBuxwVsP4XcoK5/GcGTRk6STSuk1KYrFIz9PCapnIsLtVMcD5tu9qE2WdKoTyurGWtdP2XKz43OkRoi26aG6+xZ1OqBx3Ncg5lFKE2HBPKOj9il75DjiTO+j+2EsdDddPbe0yEkJh/X17K+kzrAgNGKYDGIQhgMZNM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160212; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Z2jKFiN08fRsXOaMTsgf+6IZcnZctyjQwYVDATN5P8Y=; b=D5owM9jXypQ24uO3EJZTl6aqR2MnzL4aKNgfkVxl3HGR9me9UIxjqacOJs9z/pP5UJlWuuJbtifjf/tJFHCQKJ/C1RgKngGxf7iA3Qtcv7Eqw/XkAk19iN3UM0mQ6gUSZ0V4r+TEB0tjNzDP7BUKAFv+pkf9tW2N1/vnkkIDtAQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160212270547.3905281129337; Tue, 10 Mar 2026 09:30:12 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv8-0003Kh-QK; Tue, 10 Mar 2026 12:26:58 -0400 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 1vzzuy-0003FP-Og for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:50 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzux-0000qP-67 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:48 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-615-pBiNELYNPReY9K5t4I2JlA-1; Tue, 10 Mar 2026 12:26:42 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E39C71955E87; Tue, 10 Mar 2026 16:26:41 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BD90419560A6; Tue, 10 Mar 2026 16:26:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160006; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Z2jKFiN08fRsXOaMTsgf+6IZcnZctyjQwYVDATN5P8Y=; b=IQDPej7PFSjlLvjN3BFUzAfNas7Btr4UdJj4zu6OSTjJvFcF1BSHHYYmovHU5srAhSp47d rUbNnaQw32ALojXS2RVjZd5iQCHRWKTytzjVosTWvlXgidL7MgFmNxGFnXy9+tGPrGUXJa Y9+LyHYA5BH2+X0S0a4huyTyn7nyEFc= X-MC-Unique: pBiNELYNPReY9K5t4I2JlA-1 X-Mimecast-MFC-AGG-ID: pBiNELYNPReY9K5t4I2JlA_1773160002 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 07/28] fuse: Fix mount options Date: Tue, 10 Mar 2026 17:26:01 +0100 Message-ID: <20260310162622.333137-8-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160215081154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Since I actually took a look into how mounting with libfuse works[1], I now know that the FUSE mount options are not exactly standard mount system call options. Specifically: - We should add "nosuid,nodev,noatime" because that is going to be translated into the respective MS_ mount flags; and those flags make sense for us. - We can set rw/ro to make the mount writable or not. It makes sense to set this flag to produce a better error message for read-only exports (EROFS instead of EACCES). This changes behavior as can be seen in iotest 308: It is no longer possible to modify metadata of read-only exports. Similarly, in fuse-allow-other, we must now make the export writable to use SETATTR. In addition, in the comment, we can note that the FUSE mount() system call actually expects some more parameters that we can omit because fusermount3 (i.e. libfuse) will figure them out by itself: - fd: /dev/fuse fd - rootmode: Inode mode of the root node - user_id/group_id: Mounter's UID/GID [1] It invokes fusermount3, an SUID libfuse helper program, which parses and processes some mount options before actually invoking the mount() system call. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-8-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 14 +++++++++++--- tests/qemu-iotests/308 | 4 ++-- tests/qemu-iotests/308.out | 3 ++- tests/qemu-iotests/tests/fuse-allow-other | 3 ++- tests/qemu-iotests/tests/fuse-allow-other.out | 9 ++++++--- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 82560ca071f..0422cf4b8af 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -246,10 +246,18 @@ static int mount_fuse_export(FuseExport *exp, Error *= *errp) int ret; =20 /* - * max_read needs to match what fuse_init() sets. - * max_write need not be supplied. + * Note that these mount options differ from what we would pass to a d= irect + * mount() call: + * - nosuid, nodev, and noatime are not understood by the kernel; libf= use + * uses those options to construct the mount flags (MS_*) + * - The FUSE kernel driver requires additional options (fd, rootmode, + * user_id, group_id); these will be set by libfuse. + * Note that max_read is set here, while max_write is set via the FUSE= INIT + * operation. */ - mount_opts =3D g_strdup_printf("max_read=3D%zu,default_permissions%s", + mount_opts =3D g_strdup_printf("%s,nosuid,nodev,noatime,max_read=3D%zu= ," + "default_permissions%s", + exp->writable ? "rw" : "ro", FUSE_MAX_BOUNCE_BYTES, exp->allow_other ? ",allow_other" : ""); =20 diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 6eced3aefb9..033d5cbe222 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -178,7 +178,7 @@ stat -c 'Permissions pre-chmod: %a' "$EXT_MP" chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt stat -c 'Permissions post-+w: %a' "$EXT_MP" =20 -# But that we can set, say, +x (if we are so inclined) +# Same for other flags, like, say +x chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt stat -c 'Permissions post-+x: %a' "$EXT_MP" =20 @@ -236,7 +236,7 @@ output=3D$($QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TE= ST_IMG" 2>&1 \ =20 # Expected reference output: Opening the file fails because it has no # write permission -reference=3D"Could not open 'TEST_DIR/t.IMGFMT': Permission denied" +reference=3D"Could not open 'TEST_DIR/t.IMGFMT': Read-only file system" =20 if echo "$output" | grep -q "$reference"; then echo "Writing to read-only export failed: OK" diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index e5e233691d6..aa96faab6d0 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -53,7 +53,8 @@ Images are identical. Permissions pre-chmod: 400 chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file sy= stem Permissions post-+w: 400 -Permissions post-+x: 500 +chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file sy= stem +Permissions post-+x: 400 =20 =3D=3D=3D Mount over existing file =3D=3D=3D {'execute': 'block-export-add', diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests= /tests/fuse-allow-other index 19f494aefb1..eaa39f8f236 100755 --- a/tests/qemu-iotests/tests/fuse-allow-other +++ b/tests/qemu-iotests/tests/fuse-allow-other @@ -101,7 +101,8 @@ run_permission_test() =20 fuse_export_add 'export' \ "'mountpoint': '$EXT_MP', - 'allow-other': '$1'" + 'allow-other': '$1', + 'writable': true" =20 # Should always work echo '(Removing all permissions)' diff --git a/tests/qemu-iotests/tests/fuse-allow-other.out b/tests/qemu-iot= ests/tests/fuse-allow-other.out index 3219fc35e05..62660b40bfc 100644 --- a/tests/qemu-iotests/tests/fuse-allow-other.out +++ b/tests/qemu-iotests/tests/fuse-allow-other.out @@ -12,7 +12,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=3DIMGFMT size=3D65536 'id': 'export', 'node-name': 'node-format', 'mountpoint': 'TEST_DIR/fuse-export', - 'allow-other': 'off' + 'allow-other': 'off', + 'writable': true } } {"return": {}} (Removing all permissions) @@ -41,7 +42,8 @@ stat: cannot statx 'fuse-export': Permission denied 'id': 'export', 'node-name': 'node-format', 'mountpoint': 'TEST_DIR/fuse-export', - 'allow-other': 'on' + 'allow-other': 'on', + 'writable': true } } {"return": {}} (Removing all permissions) @@ -68,7 +70,8 @@ Permissions seen by nobody: 440 'id': 'export', 'node-name': 'node-format', 'mountpoint': 'TEST_DIR/fuse-export', - 'allow-other': 'auto' + 'allow-other': 'auto', + 'writable': true } } {"return": {}} (Removing all permissions) --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160075; cv=none; d=zohomail.com; s=zohoarc; b=G7tEOGOoLQRCsbyeEUj7hv5vISEu6qgEif0UVjVoE1LEYTn3dMJruvpZF3m3gan3a6PH85lBQJBDjQv2zSI/fhWOwepsxVvwBcS6f2HkNXSRCKDXiNDW0vfteu9bowAjDnuSZ1S6RWTb4epmjba6dnw5emWR9ixI0iHqK5JZPw8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160075; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Ohf+Uy3Gosq8fyVKO9vyFqlFBUKgEQiVJI0uE3Z6pYA=; b=oLyHDMcHYljI0oFNxeKWL8T8NNVll+IUwr2db/dRPZjPvnn1m24C7AhJZ0m0ma64sp151Yk4vRcMNn0iDcIqyZFUkpBVaaY1zAaskBsDRLvwcnUdnWEGM0Lgbsr6OSaHTjWh7uydlnoOmWGoNI96+S+BKV2HjcM3SAdhrhxd3TI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160075031561.3903994233727; Tue, 10 Mar 2026 09:27:55 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvC-0003NR-LL; Tue, 10 Mar 2026 12:27:02 -0400 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 1vzzv1-0003G2-27 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:51 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzuz-0000rb-Gs for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:50 -0400 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-295-MOSoUY96MY2-nt3yy0UQ6g-1; Tue, 10 Mar 2026 12:26:44 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7DBCE195609E; Tue, 10 Mar 2026 16:26:43 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 585E119560A6; Tue, 10 Mar 2026 16:26:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160008; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Ohf+Uy3Gosq8fyVKO9vyFqlFBUKgEQiVJI0uE3Z6pYA=; b=XspwunbP2wQ9dwU7CgG6S+/+wtxHzxfWcU7HeepZZZ91EYlR7Ba3F9KnVBcVJHSiKPJpxz vPqGu6+Md1Cg6K4wxCn+atXIQDjnPRGbAeTUFLUTadPyIvv/wfUzADhVF674ksV25Uvof+ p0bngP4V2K5/AKGUJ3Mji8HzDg5A4g0= X-MC-Unique: MOSoUY96MY2-nt3yy0UQ6g-1 X-Mimecast-MFC-AGG-ID: MOSoUY96MY2-nt3yy0UQ6g_1773160003 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 08/28] fuse: Set direct_io and parallel_direct_writes Date: Tue, 10 Mar 2026 17:26:02 +0100 Message-ID: <20260310162622.333137-9-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160076684154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek In fuse_open(), set these flags: - direct_io: We probably actually don't want to have the host page cache be used for our exports. QEMU block exports are supposed to represent the image as-is (and thus potentially changing). This causes a change in iotest 308's reference output. - parallel_direct_writes: We can (now) cope with parallel writes, so we should set this flag. For some reason, it doesn't seem to make an actual performance difference with libfuse, but it does make a difference without it, so let's set it. (See "fuse: Copy write buffer content before polling" for further discussion.) Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-9-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 2 ++ tests/qemu-iotests/308.out | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 0422cf4b8af..d0e3c6bf61f 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -582,6 +582,8 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino= de, struct stat *statbuf, static void fuse_open(fuse_req_t req, fuse_ino_t inode, struct fuse_file_info *fi) { + fi->direct_io =3D true; + fi->parallel_direct_writes =3D true; fuse_reply_open(req, fi); } =20 diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index aa96faab6d0..2d7a38d63d2 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -131,7 +131,7 @@ wrote 65536/65536 bytes at offset 1048576 =20 --- Try growing non-growable export --- (OK: Lengths of export and original are the same) -dd: error writing 'TEST_DIR/t.IMGFMT.fuse': Input/output error +dd: error writing 'TEST_DIR/t.IMGFMT.fuse': No space left on device 1+0 records in 0+0 records out =20 --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160172; cv=none; d=zohomail.com; s=zohoarc; b=QZTMe1kF+bXXkk7WlIEf8gdJzz6MMfsNrlBB44Lc8FWBx+jknlnieB4bO0cp0rfFeMauqsaEtgHfagXS7pvZZZORrs7ZJcBEjXUHipA1O1GWGgFY/kJEU3z0DCTvGkqVQU83uTy99tM716++xVsbYNBroYa+A5dwVBeBUVQrzlM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160172; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=hFMufQi+XycQE9Qf84tk8R+/pbIkQHEN0vCiwwf4B4U=; b=M8P15C0GUiMq9QfqOzh4gJfrmII22LtRYCxRvYZ3CGmGL9LwZ5dTe1fsVjPHGVE1ptlkrnmlEp6FVmTY9pqDQ4t8lj24OLEOoxdd0XFMNQZ3P1e4KrCQWMSz1Kwrv9/cTCMwXsM9x7q9kTxeAwr7HvszeMXItp+r6H8CeKojPVQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160172681756.1474869592206; Tue, 10 Mar 2026 09:29:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv8-0003Kd-MZ; Tue, 10 Mar 2026 12:26:58 -0400 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 1vzzv0-0003Fg-0M for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:50 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzux-0000qt-VP for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:49 -0400 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-230-OBch-ShNP7KR6UeYbzJ1ag-1; Tue, 10 Mar 2026 12:26:46 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 19F081955DE2; Tue, 10 Mar 2026 16:26:45 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E98BB19560A6; Tue, 10 Mar 2026 16:26:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160007; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hFMufQi+XycQE9Qf84tk8R+/pbIkQHEN0vCiwwf4B4U=; b=dqcIEMpqcX/khR44punntOqPXFKVRr0G/DEOtGu7UlbZPsmP6LaGtphws0/y4NfMGtW5jR g8sdiLGbF7VOg2b3Rhb2J9XiYMdh7IxGVt7QO3iZSrObWKC0ZwZigrmBqmOcVqwE+wpukc CzOtCD1Hdp2vHvbAqAcyFg62r3F4ogE= X-MC-Unique: OBch-ShNP7KR6UeYbzJ1ag-1 X-Mimecast-MFC-AGG-ID: OBch-ShNP7KR6UeYbzJ1ag_1773160005 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 09/28] fuse: Introduce fuse_{at,de}tach_handlers() Date: Tue, 10 Mar 2026 17:26:03 +0100 Message-ID: <20260310162622.333137-10-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160174266154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Pull setting up and tearing down the AIO context handlers into two dedicated functions. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-10-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index d0e3c6bf61f..5953407f20f 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -78,27 +78,34 @@ static void read_from_fuse_export(void *opaque); static bool is_regular_file(const char *path, Error **errp); =20 =20 -static void fuse_export_drained_begin(void *opaque) +static void fuse_attach_handlers(FuseExport *exp) { - FuseExport *exp =3D opaque; + aio_set_fd_handler(exp->common.ctx, + fuse_session_fd(exp->fuse_session), + read_from_fuse_export, NULL, NULL, NULL, exp); + exp->fd_handler_set_up =3D true; +} =20 +static void fuse_detach_handlers(FuseExport *exp) +{ aio_set_fd_handler(exp->common.ctx, fuse_session_fd(exp->fuse_session), NULL, NULL, NULL, NULL, NULL); exp->fd_handler_set_up =3D false; } =20 +static void fuse_export_drained_begin(void *opaque) +{ + fuse_detach_handlers(opaque); +} + static void fuse_export_drained_end(void *opaque) { FuseExport *exp =3D opaque; =20 /* Refresh AioContext in case it changed */ exp->common.ctx =3D blk_get_aio_context(exp->common.blk); - - aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), - read_from_fuse_export, NULL, NULL, NULL, exp); - exp->fd_handler_set_up =3D true; + fuse_attach_handlers(exp); } =20 static bool fuse_export_drained_poll(void *opaque) @@ -209,11 +216,7 @@ static int fuse_export_create(BlockExport *blk_exp, =20 g_hash_table_insert(exports, g_strdup(exp->mountpoint), NULL); =20 - aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), - read_from_fuse_export, NULL, NULL, NULL, exp); - exp->fd_handler_set_up =3D true; - + fuse_attach_handlers(exp); return 0; =20 fail: @@ -335,10 +338,7 @@ static void fuse_export_shutdown(BlockExport *blk_exp) fuse_session_exit(exp->fuse_session); =20 if (exp->fd_handler_set_up) { - aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), - NULL, NULL, NULL, NULL, NULL); - exp->fd_handler_set_up =3D false; + fuse_detach_handlers(exp); } } =20 --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160171; cv=none; d=zohomail.com; s=zohoarc; b=N13yI7g7dhuBxZDgeKWPUtG1hVj3h4NOXglZh3+8TF+570OYeiLdDE0NOOU5ZxuaCxPTj9w6mBuWW/s9Q8Kz7RxW8uidwq+06A0lg0dybLosCy3zkWk0PFCBBKe2fISJLO5Sn7TKZMp9DSq2Q3qgRmCyLsf+F8BU8jwEwQgBp34= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160171; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=BZk3YUP5SpVqzh6hMV3KaqxznPXV0KWA8O2wgLzS6ps=; b=ZFwPXTGS/IovURQrDvjostoR66t0TMTV+NedYxzkw+1QhrGz5EREPTs5AryVmzhI4Nus/TSsmh3FhMRHXm8UTuFlzztYclJhxFZxcV/xnV94q7OnzHr/eqeIq5kvXgNVKh1Vu3dmPKDm75mKEuoJoXaeXHsDzc6YMICBB5g9JfQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160171507555.0823740195228; Tue, 10 Mar 2026 09:29:31 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvS-0003WI-L5; Tue, 10 Mar 2026 12:27:18 -0400 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 1vzzv2-0003GK-Ra for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:53 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzv1-0000sd-GX for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:52 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-185-1hXKAtjPNAWB4_Z61yJ2Jw-1; Tue, 10 Mar 2026 12:26:47 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A8FB419560B3; Tue, 10 Mar 2026 16:26:46 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8402919560A6; Tue, 10 Mar 2026 16:26:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160010; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BZk3YUP5SpVqzh6hMV3KaqxznPXV0KWA8O2wgLzS6ps=; b=AnChs8li1PlyF4EcUclKg+JVlPZ28x9D19aHWPpvyEQgo+8ZbnmtGAk/J+gE98cInhqTWz wzxMzF4fKLm14uPKXVIHpAWLRJWkKEVGB12fuiiA1rWBpzZ2pekt13Vwx6qeOLyr6gVFxm 5PFA62hdCb/ZBUI3+1DwWQ/6mtWjeMY= X-MC-Unique: 1hXKAtjPNAWB4_Z61yJ2Jw-1 X-Mimecast-MFC-AGG-ID: 1hXKAtjPNAWB4_Z61yJ2Jw_1773160006 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 10/28] fuse: Introduce fuse_{inc,dec}_in_flight() Date: Tue, 10 Mar 2026 17:26:04 +0100 Message-ID: <20260310162622.333137-11-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160173234158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek This is how vduse-blk.c does it, and it does seem better to have dedicated functions for it. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-11-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 5953407f20f..fc75a5e74d9 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -78,6 +78,25 @@ static void read_from_fuse_export(void *opaque); static bool is_regular_file(const char *path, Error **errp); =20 =20 +static void fuse_inc_in_flight(FuseExport *exp) +{ + if (qatomic_fetch_inc(&exp->in_flight) =3D=3D 0) { + /* Prevent export from being deleted */ + blk_exp_ref(&exp->common); + } +} + +static void fuse_dec_in_flight(FuseExport *exp) +{ + if (qatomic_fetch_dec(&exp->in_flight) =3D=3D 1) { + /* Wake AIO_WAIT_WHILE() */ + aio_wait_kick(); + + /* Now the export can be deleted */ + blk_exp_unref(&exp->common); + } +} + static void fuse_attach_handlers(FuseExport *exp) { aio_set_fd_handler(exp->common.ctx, @@ -303,9 +322,7 @@ static void read_from_fuse_export(void *opaque) FuseExport *exp =3D opaque; int ret; =20 - blk_exp_ref(&exp->common); - - qatomic_inc(&exp->in_flight); + fuse_inc_in_flight(exp); =20 do { ret =3D fuse_session_receive_buf(exp->fuse_session, &exp->fuse_buf= ); @@ -323,11 +340,7 @@ static void read_from_fuse_export(void *opaque) fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); =20 out: - if (qatomic_fetch_dec(&exp->in_flight) =3D=3D 1) { - aio_wait_kick(); /* wake AIO_WAIT_WHILE() */ - } - - blk_exp_unref(&exp->common); + fuse_dec_in_flight(exp); } =20 static void fuse_export_shutdown(BlockExport *blk_exp) --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160154; cv=none; d=zohomail.com; s=zohoarc; b=T5AcNgycbsPqJnqGd9Majnkda7LBY29+r+TQd1xgmWXJrkomZk+K2UnVCMw87yUWIXwfKhuOC1TdUlTbj7XSGDCSI+JiY/wg09UPUQrAVtuWR6xneTxtqbSP7KfrhxJocorN4bejkr/Q+WGw/ifvjEdqxSEC8U/01xmNxtB2PTE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160154; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=sCdxqzzqW+CcsOoWHKJz2eNl+Nl/2EEfwXKHZ5AnWwM=; b=OOdCA7778vdYK0VZ4UJLn+GGa8x8yBfmGVPTCIy4XnFs1uakj+0ubjYIPVHQhak5xjsDdpEMLHp6MCgEeG8R8n4metqlh+FHZURae9Gd6IgxP1BqpoeyNw2pwuiQj0VAJNSFa2zOEdyZIfrVxpj/uu7xrwm5ZxyHOfU1os9XlgI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160154586656.7651287052076; Tue, 10 Mar 2026 09:29:14 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvO-0003S6-Jp; Tue, 10 Mar 2026 12:27:14 -0400 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 1vzzv4-0003Gw-Kx for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:55 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzv2-0000t6-V5 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:54 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-262-E-Scm9PxPxaos7yX-tk5aQ-1; Tue, 10 Mar 2026 12:26:49 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 606061956089; Tue, 10 Mar 2026 16:26:48 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1FE7A19560A6; Tue, 10 Mar 2026 16:26:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160012; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sCdxqzzqW+CcsOoWHKJz2eNl+Nl/2EEfwXKHZ5AnWwM=; b=KebrbOXIAa2GgnaNln78tnA2WOTkFH+mvOMgk5a4tthZAarF6xxsXnsWow2KBohUS4AlXT 2xbIvl5VHrOhH1q6XlEhJ6miN+hgPnxRPAa+D1AQ0vxz4/Wg55QvphDEAtSceRymJubq62 M7AUq4nTaFf1njQ7NfLn7o96dsjWR2U= X-MC-Unique: E-Scm9PxPxaos7yX-tk5aQ-1 X-Mimecast-MFC-AGG-ID: E-Scm9PxPxaos7yX-tk5aQ_1773160008 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 11/28] fuse: Add halted flag Date: Tue, 10 Mar 2026 17:26:05 +0100 Message-ID: <20260310162622.333137-12-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160155180158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek This is a flag that we will want when processing FUSE requests ourselves: When the kernel sends us e.g. a truncated request (i.e. we receive less data than the request's indicated length), we cannot rely on subsequent data to be valid. Then, we are going to set this flag, halting all FUSE request processing. We plan to only use this flag in cases that would effectively be kernel bugs. While not necessary yet, access the flag atomically so that it will be safe to use once we introduce multi-threading. (Right now, the flag is unused because libfuse still does our request processing.) Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-12-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/block/export/fuse.c b/block/export/fuse.c index fc75a5e74d9..f6a5f4fa0a0 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -53,6 +53,13 @@ typedef struct FuseExport { unsigned int in_flight; /* atomic */ bool mounted, fd_handler_set_up; =20 + /* + * Set when there was an unrecoverable error and no requests should be= read + * from the device anymore (basically only in case of something we wou= ld + * consider a kernel bug). Access atomically. + */ + bool halted; + char *mountpoint; bool writable; bool growable; @@ -69,6 +76,7 @@ static const struct fuse_lowlevel_ops fuse_ops; =20 static void fuse_export_shutdown(BlockExport *exp); static void fuse_export_delete(BlockExport *exp); +static void fuse_export_halt(FuseExport *exp) G_GNUC_UNUSED; =20 static void init_exports_table(void); =20 @@ -99,6 +107,10 @@ static void fuse_dec_in_flight(FuseExport *exp) =20 static void fuse_attach_handlers(FuseExport *exp) { + if (qatomic_read(&exp->halted)) { + return; + } + aio_set_fd_handler(exp->common.ctx, fuse_session_fd(exp->fuse_session), read_from_fuse_export, NULL, NULL, NULL, exp); @@ -322,6 +334,10 @@ static void read_from_fuse_export(void *opaque) FuseExport *exp =3D opaque; int ret; =20 + if (unlikely(qatomic_read(&exp->halted))) { + return; + } + fuse_inc_in_flight(exp); =20 do { @@ -380,6 +396,20 @@ static void fuse_export_delete(BlockExport *blk_exp) g_free(exp->mountpoint); } =20 +/** + * Halt the export: Detach FD handlers, and set exp->halted to true, preve= nting + * fuse_attach_handlers() from re-attaching them, therefore stopping all f= urther + * request processing. + * + * Call this function when an unrecoverable error happens that makes proce= ssing + * all future requests unreliable. + */ +static void fuse_export_halt(FuseExport *exp) +{ + qatomic_set(&exp->halted, true); + fuse_detach_handlers(exp); +} + /** * Check whether @path points to a regular file. If not, put an * appropriate message into *errp. --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160078; cv=none; d=zohomail.com; s=zohoarc; b=Mn9E5ThN7sHGLYC6b2w7jx40mLPmQpFtqzLQOpFw7we+L9aRtXHkWp6KjHu7VzrNgkO1ZGXWVr7BdqLDpD82US+xZMzoabxaxorFxGI96729SObYJgWItMkm9EUWSePU4jox5r+5MR5jjkmqqhI5sXsHSIe/fhUnxemSoySp8fk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160078; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=CFwxAjeLT8PRQo1K5MQJorT6GXi794PGg1MNpaE1iJA=; b=gAcUpBIlzuJqHLhz9jL687CyLouxvvvCdJlaVx89OTsCkFrCCNHuylw4qSq9K3UTMqCTxCoXBLapn0fCUAtIR/T5BvgcogloOuhUIEChxdSM3bcsB3p21O/FTkKSGry0C2TFdrXQ1zpz7ZbbZZZpXvUzPPwICUn1cugOh6Sl40g= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160078078219.6308012713156; Tue, 10 Mar 2026 09:27:58 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzv9-0003L8-77; Tue, 10 Mar 2026 12:26:59 -0400 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 1vzzv6-0003Hl-2k for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:56 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzv4-0000tU-LL for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:55 -0400 Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-492-5pfRH50xN3G0jBvJ7XpA9Q-1; Tue, 10 Mar 2026 12:26:51 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id EDEFC1800756; Tue, 10 Mar 2026 16:26:49 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C97F219560A6; Tue, 10 Mar 2026 16:26:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CFwxAjeLT8PRQo1K5MQJorT6GXi794PGg1MNpaE1iJA=; b=ASDlx6loe/OnGVPFQU6o4XxEmlyntgUOOIHUena+9oIV+NN9JctCwLvE/Tln/Xbxd3qRFY mHY/dIQF9YkGXhC23gVZ0yZkWvBIezmpv04tNswVZYH9faR+CQoN9JtzzqWZdpcE39snrM ROMvgqUtc3vH05VaJo36b/i/DbtJESU= X-MC-Unique: 5pfRH50xN3G0jBvJ7XpA9Q-1 X-Mimecast-MFC-AGG-ID: 5pfRH50xN3G0jBvJ7XpA9Q_1773160010 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 12/28] fuse: fuse_{read,write}: Rename length to blk_len Date: Tue, 10 Mar 2026 17:26:06 +0100 Message-ID: <20260310162622.333137-13-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160078822158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek The term "length" is ambiguous, use "blk_len" instead to be clear. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-13-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index f6a5f4fa0a0..d45c6b814fe 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -637,7 +637,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t inode, size_t size, off_t offset, struct fuse_file_info *fi) { FuseExport *exp =3D fuse_req_userdata(req); - int64_t length; + int64_t blk_len; void *buf; int ret; =20 @@ -651,14 +651,14 @@ static void fuse_read(fuse_req_t req, fuse_ino_t inod= e, * Clients will expect short reads at EOF, so we have to limit * offset+size to the image length. */ - length =3D blk_getlength(exp->common.blk); - if (length < 0) { - fuse_reply_err(req, -length); + blk_len =3D blk_getlength(exp->common.blk); + if (blk_len < 0) { + fuse_reply_err(req, -blk_len); return; } =20 - if (offset + size > length) { - size =3D length - offset; + if (offset + size > blk_len) { + size =3D blk_len - offset; } =20 buf =3D qemu_try_blockalign(blk_bs(exp->common.blk), size); @@ -685,7 +685,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode= , const char *buf, { FuseExport *exp =3D fuse_req_userdata(req); QEMU_AUTO_VFREE void *copied =3D NULL; - int64_t length; + int64_t blk_len; int ret; =20 /* Limited by max_write, should not happen */ @@ -711,13 +711,13 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino= de, const char *buf, * Clients will expect short writes at EOF, so we have to limit * offset+size to the image length. */ - length =3D blk_getlength(exp->common.blk); - if (length < 0) { - fuse_reply_err(req, -length); + blk_len =3D blk_getlength(exp->common.blk); + if (blk_len < 0) { + fuse_reply_err(req, -blk_len); return; } =20 - if (offset + size > length) { + if (offset + size > blk_len) { if (exp->growable) { ret =3D fuse_do_truncate(exp, offset + size, true, PREALLOC_MO= DE_OFF); if (ret < 0) { @@ -725,7 +725,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode= , const char *buf, return; } } else { - size =3D length - offset; + size =3D blk_len - offset; } } =20 --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160123; cv=none; d=zohomail.com; s=zohoarc; b=IOGOcHmEbNZ/yk1V+dHQprCHnhrCDoOb1rMiNjSKLafuXTyYKic3YeDFaZaMg+DujwOt1DWhCpCE+++VDPL4zlpBVFW3DQqSIBVWsFMSo1rayPLT/yTllT94Grtci6QgTK9bflUmCX9TBtWznQJ7cxfJqVV3pTmzl6/jeV1dRx0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160123; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=a90dg7Kpj66lfcCXYa3gY/ZRYMIGRWa/2aZcC7ix4io=; b=eVS7CBTCw6LryNOxfzGUPJxT0LIX9ox9DzPFMgSOUfObxstC2fxsgY47Q9Bp1AkKvPzKpjdsZ2ezOchMnGQO6TEOYY1X+qNC7pIhlrzJu1BE+7XIXpXXFLrUN5qbA2Y18ByX3DO64p0b8xHJ1HCqVIouBF4qgRMSd1mRRLn8gXk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160123747162.2224428160565; Tue, 10 Mar 2026 09:28:43 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvV-0003eP-Uv; Tue, 10 Mar 2026 12:27:21 -0400 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 1vzzv6-0003Hm-38 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:56 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzv4-0000tW-LF for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:55 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-446-C9FmgeQHPm-YlMCdK9MRPw-1; Tue, 10 Mar 2026 12:26:52 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8ADFA195608F; Tue, 10 Mar 2026 16:26:51 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6437B19560A6; Tue, 10 Mar 2026 16:26:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=a90dg7Kpj66lfcCXYa3gY/ZRYMIGRWa/2aZcC7ix4io=; b=IFZz+lpbi/awAqHEtsLKifwuHvKOmOZq7nch9Y/co6jou46MbRFl0em7Vxu7ndq/zhHKVd s6BGMxFmXUssGpGJwwZlXfGjlSWK3NwLRV0GiIyMMYh26kXk6m8jKw2URfv8GQQxpA8s1X hwOEjAJ/CRav9/x9Ih8hQe/20+OqVtM= X-MC-Unique: C9FmgeQHPm-YlMCdK9MRPw-1 X-Mimecast-MFC-AGG-ID: C9FmgeQHPm-YlMCdK9MRPw_1773160011 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 13/28] iotests/308: Use conv=notrunc to test growability Date: Tue, 10 Mar 2026 17:26:07 +0100 Message-ID: <20260310162622.333137-14-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160124964158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Without conv=3Dnotrunc, dd will automatically truncate the output file to the @seek value at least. We want to test post-EOF I/O, not truncate, so pass conv=3Dnotrunc. (It does not make a difference in practice because we only seek to the EOF, so the truncate effectively does nothing, but this is still cleaner.) Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-14-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/308 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 033d5cbe222..6ecb275555a 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -296,7 +296,8 @@ orig_disk_usage=3D$(disk_usage "$TEST_IMG") # Should fail (exports are non-growable by default) # (Note that qemu-io can never write beyond the EOF, so we have to use # dd here) -dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D64k seek=3D$orig_len 2>&1 \ +dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D64k seek=3D$orig_len \ + conv=3Dnotrunc 2>&1 \ | _filter_testdir | _filter_imgfmt =20 echo @@ -333,7 +334,7 @@ fuse_export_add \ 'node-protocol' =20 # Now we should be able to write beyond the EOF -dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D64k seek=3D$new_len 2>&1 \ +dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D64k seek=3D$new_len conv= =3Dnotrunc 2>&1 \ | _filter_testdir | _filter_imgfmt =20 new_len=3D$(get_proto_len "$EXT_MP" "$TEST_IMG") --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160172; cv=none; d=zohomail.com; s=zohoarc; b=mKD1WBpbdxsrWiv0eF2jRTXbHIDhXmSgTz+5U/LlKuJ5lxG1EBwCgIFZdazMkj5mnBk9ZByWlKxvqHkvIT/9X3EMkpX32tWWSP7HIRZeCdgqwDm0QqQZfpnadTI9h5VvmKsbQSSiMmmz9YA1qzgpmP+bqcVaPXksnKmiUC1E4vs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160172; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=M5HmUWnkEJbA+La4dJHbp0RAGgMPp0DV1nggQsvosNE=; b=m+5WokwI+sK2gt6Ub67861pjB8VhFwh4045tKQT5KMGOW7dK2+pmLvci/vqwxY0SLnwWadFXleK/cNKudsUpyKV6O9Ddev1VJR8h9OD9uAcftEvMtrGv7CiAVKx1/ChOihZWLKttAkQkkJSggMW9beQkIQSOBtYyjwiTY3D5jms= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160172003553.5225617062418; Tue, 10 Mar 2026 09:29:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvO-0003S7-SO; Tue, 10 Mar 2026 12:27:15 -0400 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 1vzzv9-0003Le-GG for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:59 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzv7-0000uN-RB for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:59 -0400 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-518-uTwkAAJrM8Cwcqq-skD6Vg-1; Tue, 10 Mar 2026 12:26:54 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 27F701956060; Tue, 10 Mar 2026 16:26:53 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0014819560A6; Tue, 10 Mar 2026 16:26:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160017; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=M5HmUWnkEJbA+La4dJHbp0RAGgMPp0DV1nggQsvosNE=; b=ccK1SaMTIavWbPuiAMmQ0LYr8jPMaZKqW4puXS5xi7tmsXn1uV3GM1K+0E71JBG7H3GZ7j o3Zy+cHPihgNz6EcU3mfy3QcbfWwHEnX0IWObltnfKYBc5VnNDFjx/qfkQ0DOAg3KK8Hko FykWoAwVBfFI+R4JuznrzWNZHD7BIAg= X-MC-Unique: uTwkAAJrM8Cwcqq-skD6Vg-1 X-Mimecast-MFC-AGG-ID: uTwkAAJrM8Cwcqq-skD6Vg_1773160013 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 14/28] fuse: Explicitly handle non-grow post-EOF accesses Date: Tue, 10 Mar 2026 17:26:08 +0100 Message-ID: <20260310162622.333137-15-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160174389154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek When reading to / writing from non-growable exports, we cap the I/O size by `offset - blk_len`. This will underflow for accesses that are completely past the disk end. Check and handle that case explicitly. This is also enough to ensure that `offset + size` will not overflow; blk_len is int64_t, offset is uint32_t, `offset < blk_len`, so from `INT64_MAX + UINT32_MAX < UINT64_MAX` it follows that `offset + size` cannot overflow. Just one catch: We have to allow write accesses to growable exports past the EOF, so then we cannot rely on `offset < blk_len`, but have to verify explicitly that `offset + size` does not overflow. The negative consequences of not having this commit are luckily limited because blk_pread() and blk_pwrite() will reject post-EOF requests anyway, so a `size` underflow post-EOF will just result in an I/O error. So: - Post-EOF reads will incorrectly result in I/O errors instead of just 0-length reads. We will also attempt to allocate a very large buffer, which is wrong and not good, but not terrible. - Post-EOF writes on non-growable exports will result in I/O errors instead of 0-length writes (which generally indicate ENOSPC). - Post-EOF writes on growable exports can theoretically overflow on EOF and truncate the export down to a much too small size, but in practice, FUSE will never send an offset greater than signed INT_MAX, preventing a uint64_t overflow. (fuse_write_args_fill() in the kernel uses loff_t for the offset, which is signed.) Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-15-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 20 +++++++++++++++++++- tests/qemu-iotests/308 | 35 ++++++++++++++++++++++++++++++----- tests/qemu-iotests/308.out | 10 ++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index d45c6b814fe..af0a8de17b1 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -657,6 +657,16 @@ static void fuse_read(fuse_req_t req, fuse_ino_t inode, return; } =20 + if (offset >=3D blk_len) { + /* + * Technically libfuse does not allow returning a zero error code = for + * read requests, but in practice this is a 0-length read (and a f= uture + * commit will change this code anyway) + */ + fuse_reply_err(req, 0); + return; + } + if (offset + size > blk_len) { size =3D blk_len - offset; } @@ -717,7 +727,15 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inod= e, const char *buf, return; } =20 - if (offset + size > blk_len) { + if (offset >=3D blk_len && !exp->growable) { + fuse_reply_write(req, 0); + return; + } + + if (offset + size < offset) { + fuse_reply_err(req, EINVAL); + return; + } else if (offset + size > blk_len) { if (exp->growable) { ret =3D fuse_do_truncate(exp, offset + size, true, PREALLOC_MO= DE_OFF); if (ret < 0) { diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 6ecb275555a..a83c6fc01fb 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -300,16 +300,34 @@ dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D64k s= eek=3D$orig_len \ conv=3Dnotrunc 2>&1 \ | _filter_testdir | _filter_imgfmt =20 +# And one really squarely post-EOF write +dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D1 seek=3D$((orig_len + 32 = * 1024)) \ + conv=3Dnotrunc 2>&1 \ + | _filter_testdir | _filter_imgfmt + +# Half-post-EOF reads +dd if=3D"$EXT_MP" of=3D/dev/null bs=3D1 count=3D64k skip=3D$((orig_len - 3= 2 * 1024)) \ + 2>&1 | _filter_testdir | _filter_imgfmt + +# And one really squarely post-EOF read +dd if=3D"$EXT_MP" of=3D/dev/null bs=3D1 count=3D1 skip=3D$((orig_len + 32 = * 1024)) \ + 2>&1 | _filter_testdir | _filter_imgfmt + echo echo '--- Resize export ---' =20 # But we can truncate it explicitly; even with fallocate -fallocate -o "$orig_len" -l 64k "$EXT_MP" +# (Make sure we extend it to a length not divisible by 128k, we need that = below) +bs=3D$((128 * 1024)) +extend_to=3D$(((orig_len + bs - 1) / bs * bs + bs / 2)) +extend_by=3D$((extend_to - orig_len)) + +fallocate -o "$orig_len" -l $extend_by "$EXT_MP" =20 new_len=3D$(get_proto_len "$EXT_MP" "$TEST_IMG") -if [ "$new_len" !=3D "$((orig_len + 65536))" ]; then +if [ "$new_len" !=3D "$extend_to" ]; then echo 'ERROR: Unexpected post-truncate image size:' - echo "$new_len !=3D $((orig_len + 65536))" + echo "$new_len !=3D $extend_to" else echo 'OK: Post-truncate image size is as expected' fi @@ -322,6 +340,13 @@ else echo "$orig_disk_usage =3D> $new_disk_usage" fi =20 +# Use this opportunity to test a read access across the (now no longer so = much +# aligned) EOF. dd can only do requests with a length of its block size, = and +# all of its seek/skip values are in bs units, so it is hard to do a reque= st +# across the EOF if the EOF is at a power of two (64M). +dd if=3D"$EXT_MP" of=3D/dev/null bs=3D$bs count=3D2 skip=3D$((extend_to / = bs)) \ + 2>&1 | _filter_testdir | _filter_imgfmt + echo echo '--- Try growing growable export ---' =20 @@ -338,9 +363,9 @@ dd if=3D/dev/zero of=3D"$EXT_MP" bs=3D1 count=3D64k see= k=3D$new_len conv=3Dnotrunc 2>&1 \ | _filter_testdir | _filter_imgfmt =20 new_len=3D$(get_proto_len "$EXT_MP" "$TEST_IMG") -if [ "$new_len" !=3D "$((orig_len + 131072))" ]; then +if [ "$new_len" !=3D "$((extend_to + 65536))" ]; then echo 'ERROR: Unexpected post-grow image size:' - echo "$new_len !=3D $((orig_len + 131072))" + echo "$new_len !=3D $((extend_to + 65536))" else echo 'OK: Post-grow image size is as expected' fi diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index 2d7a38d63d2..ebeaf64b486 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -134,11 +134,21 @@ wrote 65536/65536 bytes at offset 1048576 dd: error writing 'TEST_DIR/t.IMGFMT.fuse': No space left on device 1+0 records in 0+0 records out +dd: error writing 'TEST_DIR/t.IMGFMT.fuse': No space left on device +1+0 records in +0+0 records out +32768+0 records in +32768+0 records out +dd: TEST_DIR/t.IMGFMT.fuse: cannot skip to specified offset +0+0 records in +0+0 records out =20 --- Resize export --- (OK: Lengths of export and original are the same) OK: Post-truncate image size is as expected OK: Disk usage grew with fallocate +0+1 records in +0+1 records out =20 --- Try growing growable export --- {'execute': 'block-export-del', --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160066; cv=none; d=zohomail.com; s=zohoarc; b=dzfX0sz6nhmi6tI1jQAlcU39op58lO/TP4ssQM9DX3NTmy11EyDRKRHZYDLl0Yu2DkO+GNv2mR2HxyUJG8OmMtxfpvqFrY2kyG3xsjB06wyVlutC6KOgUAEEyL58gaYpVySKi5UsH4usm4EyQ2BI6HWlI7OmXj1MHGdqL9O+ulw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160066; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=EerOtERlXoVSU73mqwwBbXk9WG5MEwl6IjYw0Ivbe4o=; b=Rh5xIyP4oBbuxASzS1j7ve5hiCG1G5oOdAYfbcI1yTQGiueBRaYiAIwgCRf+Pin879Y3qttnHErlXq2rFbGlD201mlWmmeLFf4JiqDGQWWe9DRqI6MOQw/OrgHeZw/m4sHtJF/dy/i07rmm25Q852PE2ecSvv4G02w8/v0p0/rU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177316006641596.24585304818618; Tue, 10 Mar 2026 09:27:46 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvK-0003Qk-OM; Tue, 10 Mar 2026 12:27:10 -0400 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 1vzzv9-0003L3-0t for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:59 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzv7-0000uK-F9 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:26:58 -0400 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-584-AXSMoUFENKizoniwXHTxaQ-1; Tue, 10 Mar 2026 12:26:55 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B74721956094; Tue, 10 Mar 2026 16:26:54 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9157419560A6; Tue, 10 Mar 2026 16:26:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160016; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EerOtERlXoVSU73mqwwBbXk9WG5MEwl6IjYw0Ivbe4o=; b=iQN2QHJrPEpc5SuTIe1h1a3+VgzSNgNyxKHslD2TAqqimrwEIxyxHgHcI4d1CUFOXZf1Na O65iFZMi1ql7Syl5kS3zK9AyiJYXW8gdlkk3Oofi7+pJAkf+tt8AmYx3xG4Gj5/wuDJ0t3 aG6mXMfBSD4W3iHByUMW9TEFPl6bZZk= X-MC-Unique: AXSMoUFENKizoniwXHTxaQ-1 X-Mimecast-MFC-AGG-ID: AXSMoUFENKizoniwXHTxaQ_1773160014 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 15/28] block: Move qemu_fcntl_addfl() into osdep.c Date: Tue, 10 Mar 2026 17:26:09 +0100 Message-ID: <20260310162622.333137-16-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160068562154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Move file-posix's helper to add a flag (or a set of flags) to an FD's existing set of flags into osdep.c for other places to use. Suggested-by: Eric Blake Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-16-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/qemu/osdep.h | 1 + block/file-posix.c | 17 +---------------- util/osdep.c | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index b384b5b506b..f151578b5ce 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -633,6 +633,7 @@ int qemu_lock_fd(int fd, int64_t start, int64_t len, bo= ol exclusive); int qemu_unlock_fd(int fd, int64_t start, int64_t len); int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive); bool qemu_has_ofd_lock(void); +int qemu_fcntl_addfl(int fd, int flag); #endif =20 bool qemu_has_direct_io(void); diff --git a/block/file-posix.c b/block/file-posix.c index 6265d2e248b..e49b13d6abb 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1056,21 +1056,6 @@ static int raw_handle_perm_lock(BlockDriverState *bs, return ret; } =20 -/* Sets a specific flag */ -static int fcntl_setfl(int fd, int flag) -{ - int flags; - - flags =3D fcntl(fd, F_GETFL); - if (flags =3D=3D -1) { - return -errno; - } - if (fcntl(fd, F_SETFL, flags | flag) =3D=3D -1) { - return -errno; - } - return 0; -} - static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, int *open_flags, uint64_t perm, Error **e= rrp) { @@ -1109,7 +1094,7 @@ static int raw_reconfigure_getfd(BlockDriverState *bs= , int flags, /* dup the original fd */ fd =3D qemu_dup(s->fd); if (fd >=3D 0) { - ret =3D fcntl_setfl(fd, *open_flags); + ret =3D qemu_fcntl_addfl(fd, *open_flags); if (ret) { qemu_close(fd); fd =3D -1; diff --git a/util/osdep.c b/util/osdep.c index 770369831bc..000e7daac8b 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -280,6 +280,24 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t l= en, bool exclusive) return fl.l_type =3D=3D F_UNLCK ? 0 : -EAGAIN; } } + +/** + * Set the given flag(s) (fcntl GETFL/SETFL) on the given FD, while retain= ing + * other flags. + */ +int qemu_fcntl_addfl(int fd, int flag) +{ + int flags; + + flags =3D fcntl(fd, F_GETFL); + if (flags =3D=3D -1) { + return -errno; + } + if (fcntl(fd, F_SETFL, flags | flag) =3D=3D -1) { + return -errno; + } + return 0; +} #endif =20 bool qemu_has_direct_io(void) --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160075; cv=none; d=zohomail.com; s=zohoarc; b=KZ5aKbXoY7HbyEF/15UxeF974/TK+OYTOPRdNb22m45U5T6tFiuRxK9zzaJvu+Qr0UAK2o38FrDhms6U4dk+BuhGotheaMdxOF1ur/yHNpFC0Dd9JprmBAR372oudsiqehf7T6uT5drDvDrCLBUCMd0xAZ7wYQouqvdqy47z2QQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160075; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=mhdlKPgh17Z3o9lLJY65BTZtmBO2Ej1Xu+1oTHM+Z/4=; b=K77lYPyfx6e9DNRRD5QxBB2i2tw8vo/A0wAEABeSHvg28arE1a5jnhrG0fcSLBsjpYQBgUOD/vQYERjLu78iepnQcagR8TV17nUjR+BiqjhHYIWli6i7M21uZpw/kr3Sfc8tqL1oXRw62jTpk4UlIRQdq3I/f1WUIcDbQgpLNvs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177316007503571.19308311756981; Tue, 10 Mar 2026 09:27:55 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvS-0003Wa-Lv; Tue, 10 Mar 2026 12:27:18 -0400 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 1vzzvC-0003Nk-Fw for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:02 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvB-0000vX-4M for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:02 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-638-gRkn4m2FNGCUZcVOotNsPg-1; Tue, 10 Mar 2026 12:26:57 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5097A18002DD; Tue, 10 Mar 2026 16:26:56 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2B3A419560A6; Tue, 10 Mar 2026 16:26:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160020; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mhdlKPgh17Z3o9lLJY65BTZtmBO2Ej1Xu+1oTHM+Z/4=; b=gPZcYYB/BYN0BqBjbqYJF2r0ovaMq4sVRsILmpp+Tg6isG1ICtcL0SA54WMppkyuQz6t1u BwSZYYkb4L5/L8a3Llr3IenBfsLPpLOIPwUVOcSMNaXxrCp4wJfvEz9rKR0RKRSAqNd+LC Pc3FkIRAyDlE5BGRizfa9eoTOOd80Ao= X-MC-Unique: gRkn4m2FNGCUZcVOotNsPg-1 X-Mimecast-MFC-AGG-ID: gRkn4m2FNGCUZcVOotNsPg_1773160016 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 16/28] fuse: Drop permission changes in fuse_do_truncate Date: Tue, 10 Mar 2026 17:26:10 +0100 Message-ID: <20260310162622.333137-17-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160076693154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek This function is always called with writable =3D=3D true. This makes add_resize_perm always false, and thus we can drop the quite ugly permission-changing code. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-17-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index af0a8de17b1..b7a710c29f8 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -503,44 +503,14 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t i= node, static int fuse_do_truncate(const FuseExport *exp, int64_t size, bool req_zero_write, PreallocMode prealloc) { - uint64_t blk_perm, blk_shared_perm; BdrvRequestFlags truncate_flags =3D 0; - bool add_resize_perm; - int ret, ret_check; - - /* Growable and writable exports have a permanent RESIZE permission */ - add_resize_perm =3D !exp->growable && !exp->writable; =20 if (req_zero_write) { truncate_flags |=3D BDRV_REQ_ZERO_WRITE; } =20 - if (add_resize_perm) { - if (!qemu_in_main_thread()) { - /* Changing permissions like below only works in the main thre= ad */ - return -EPERM; - } - - blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm); - - ret =3D blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE, - blk_shared_perm, NULL); - if (ret < 0) { - return ret; - } - } - - ret =3D blk_truncate(exp->common.blk, size, true, prealloc, - truncate_flags, NULL); - - if (add_resize_perm) { - /* Must succeed, because we are only giving up the RESIZE permissi= on */ - ret_check =3D blk_set_perm(exp->common.blk, blk_perm, - blk_shared_perm, &error_abort); - assert(ret_check =3D=3D 0); - } - - return ret; + return blk_truncate(exp->common.blk, size, true, prealloc, + truncate_flags, NULL); } =20 /** --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160200; cv=none; d=zohomail.com; s=zohoarc; b=cTqFc492UatBuFVW6pJf7OLSKpyGf75cvV4Vn8dlkRw1/r4krSaHvgJBjA8U12PFDaEJtDPKSauP9imiENJbdw8dx2DOH3gTi22IJLTi5BLx+/RABkMki7cRn1fqPaOOgCr4qZrdcWNFoJ4/pV5d6pgRFxQkjb7td1/I4OLq8wI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160200; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=4QGtxuurDfG9K0hu6NwmWWZRGQS7y64Zx5Pn6GTm0T0=; b=Ce2VZEOokdLVPqilbi+dAz0gL7nzzc1gzi+Wk8tKFrXIoXnM71gMMqyTB+xy7DBG2WRUfVrU1VzEXl0yXgQRi8Srn/7u6ScMzWDVPxyiFgSTZU5CUxd5N1oc0QuvQgO25mfFnRIUWMxkG1mYJrJ8Pi4NW+GBaI+2HB/ZCjvcL8w= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17731602006721021.6699485268233; Tue, 10 Mar 2026 09:30:00 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvV-0003d5-JV; Tue, 10 Mar 2026 12:27:21 -0400 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 1vzzvF-0003P8-73 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:06 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvB-0000va-7r for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:04 -0400 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-669-hDFTtB1PN6eoQFA4Qxok0Q-1; Tue, 10 Mar 2026 12:26:59 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 24FF51956050; Tue, 10 Mar 2026 16:26:58 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BB54219560A6; Tue, 10 Mar 2026 16:26:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160020; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4QGtxuurDfG9K0hu6NwmWWZRGQS7y64Zx5Pn6GTm0T0=; b=ZkeJyz8nMJavBMiTxhCtPCzUhGd0cNNhF7ZMpRJKVyvcY2QZywgeU9QWwZp4hT9uKLYtlb ngpqkz4bNPK+ej39Oc3nYtHAu94tBvRtHUF80TluMHXeePZTD/ey3PhK64AG2sHydtaFzx LBOpPKD1GCOg2YyvHeOky7V5NRMQTlI= X-MC-Unique: hDFTtB1PN6eoQFA4Qxok0Q-1 X-Mimecast-MFC-AGG-ID: hDFTtB1PN6eoQFA4Qxok0Q_1773160018 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 17/28] fuse: Manually process requests (without libfuse) Date: Tue, 10 Mar 2026 17:26:11 +0100 Message-ID: <20260310162622.333137-18-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160201504158500 From: Hanna Czenczek Manually read requests from the /dev/fuse FD and process them, without using libfuse. This allows us to safely add parallel request processing in coroutines later, without having to worry about libfuse internals. (Technically, we already have exactly that problem with read_from_fuse_export()/read_from_fuse_fd() nesting.) We will continue to use libfuse for mounting the filesystem; fusermount3 is a effectively a helper program of libfuse, so it should know best how to interact with it. (Doing it manually without libfuse, while doable, is a bit of a pain, and it is not clear to me how stable the "protocol" actually is.) Take this opportunity of quite a major rewrite to update the Copyright line with corrected information that has surfaced in the meantime. Here are some benchmarks from before this patch (4k, iodepth=3D16, libaio; except 'sync', which are iodepth=3D1 and pvsync2): file: read: seq aio: 99.8k =C2=B11.5k IOPS rand aio: 50.5k =C2=B11.0k seq sync: 36.1k =C2=B11.1k rand sync: 10.0k =C2=B10.1k write: seq aio: 72.0k =C2=B19.3k rand aio: 70.6k =C2=B12.5k seq sync: 30.6k =C2=B10.8k rand sync: 30.1k=C2=A0=C2=B11.0k null: read: seq aio: 157.9k =C2=B14.7k rand aio: 158.7k =C2=B14.8k seq sync: 80.2k =C2=B12.8k rand sync: 77.5k =C2=B13.8k write: seq aio: 154.3k =C2=B13.6k rand aio: 154.3k =C2=B14.2k seq sync: 76.1k =C2=B15.2k rand sync: 72.9k =C2=B14.0k And with this patch applied: file: read: seq aio: 106.8k =C2=B11.9k (+7%) rand aio: 48.3k =C2=B18.8k (-4%) seq sync: 35.5k =C2=B11.4k (-2%) rand sync: 10.0k =C2=B10.2k (=C2=B10%) write: seq aio: 76.3k =C2=B16.6k (+6%) rand aio: 76.4k =C2=B11.5k (+8%) seq sync: 31.6k =C2=B10.6k (+3%) rand sync: 30.9k =C2=B10.8k (+3%) null: read: seq aio: 161.7k =C2=B16.0k (+2%) rand aio: 165.6k =C2=B17.1k (+4%) seq sync: 80.5k =C2=B13.0k (=C2=B10%) rand sync: 78.5k =C2=B13.1k (+1%) write: seq aio: 185.1k =C2=B13.3k (+20%) rand aio: 186.7k =C2=B14.8k (+21%) seq sync: 82.5k =C2=B14.2k (+8%) rand sync: 78.7k =C2=B13.2k (+8%) So not much difference, aside from write AIO to a null-co export getting a bit better. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-18-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 945 +++++++++++++++++++++++++++++++++----------- 1 file changed, 721 insertions(+), 224 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index b7a710c29f8..bd099d12911 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -1,7 +1,7 @@ /* * Present a block device as a raw image through FUSE * - * Copyright (c) 2020 Max Reitz + * Copyright (c) 2020, 2025 Hanna Czenczek * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,12 +27,15 @@ #include "block/qapi.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "system/block-backend.h" =20 #include #include =20 +#include "standard-headers/linux/fuse.h" + #if defined(CONFIG_FALLOCATE_ZERO_RANGE) #include #endif @@ -42,17 +45,101 @@ #endif =20 /* Prevent overly long bounce buffer allocations */ -#define FUSE_MAX_BOUNCE_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 64 * 1024 * 102= 4)) +#define FUSE_MAX_READ_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 64 * 1024 * 1024)) +#define FUSE_MAX_WRITE_BYTES (64 * 1024) + +/* + * fuse_init_in structure before 7.36. We don't need the flags2 field add= ed + * there, so we can work with the smaller older structure to stay compatib= le + * with older kernels. + */ +struct fuse_init_in_compat { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; +}; + +typedef struct FuseRequestInHeader { + struct fuse_in_header common; + /* All supported requests */ + union { + struct fuse_init_in_compat init; + struct fuse_open_in open; + struct fuse_setattr_in setattr; + struct fuse_read_in read; + struct fuse_write_in write; + struct fuse_fallocate_in fallocate; +#ifdef CONFIG_FUSE_LSEEK + struct fuse_lseek_in lseek; +#endif + }; +} FuseRequestInHeader; + +typedef struct FuseRequestOutHeader { + struct fuse_out_header common; + /* All supported requests */ + union { + struct fuse_init_out init; + struct fuse_statfs_out statfs; + struct fuse_open_out open; + struct fuse_attr_out attr; + struct fuse_write_out write; +#ifdef CONFIG_FUSE_LSEEK + struct fuse_lseek_out lseek; +#endif + }; +} FuseRequestOutHeader; + +typedef union FuseRequestInHeaderBuf { + struct FuseRequestInHeader structured; + struct { + /* + * Part of the request header that is filled for write requests + * (Needed because we want the data to go into a different buffer,= to + * avoid having to use a bounce buffer) + */ + char head[sizeof(struct fuse_in_header) + + sizeof(struct fuse_write_in)]; + /* + * Rest of the request header for requests that have a longer head= er + * than write requests + */ + char tail[sizeof(FuseRequestInHeader) - + (sizeof(struct fuse_in_header) + + sizeof(struct fuse_write_in))]; + }; +} FuseRequestInHeaderBuf; =20 +QEMU_BUILD_BUG_ON(sizeof(FuseRequestInHeaderBuf) !=3D + sizeof(FuseRequestInHeader)); +QEMU_BUILD_BUG_ON(sizeof(((FuseRequestInHeaderBuf *)0)->head) + + sizeof(((FuseRequestInHeaderBuf *)0)->tail) !=3D + sizeof(FuseRequestInHeader)); =20 typedef struct FuseExport { BlockExport common; =20 struct fuse_session *fuse_session; - struct fuse_buf fuse_buf; unsigned int in_flight; /* atomic */ bool mounted, fd_handler_set_up; =20 + /* + * Cached buffer to receive the data of WRITE requests. Cached becaus= e: + * To read requests, we put a FuseRequestInHeaderBuf (FRIHB) object on= the + * stack, and a (WRITE data) buffer on the heap. We pass FRIHB.head a= nd the + * data buffer to readv(). This way, for WRITE requests, we get exact= ly + * their data in the data buffer and can avoid bounce buffering. + * However, for non-WRITE requests, some of the header may end up in t= he + * data buffer, so we will need to copy that back into the FRIHB objec= t, and + * then we don't need the heap buffer anymore. That is why we cache i= t, so + * we can trivially reuse it between non-WRITE requests. + * + * Note that these data buffers and thus req_write_data_cached are all= ocated + * via blk_blockalign() and thus need to be freed via qemu_vfree(). + */ + void *req_write_data_cached; + /* * Set when there was an unrecoverable error and no requests should be= read * from the device anymore (basically only in case of something we wou= ld @@ -60,6 +147,8 @@ typedef struct FuseExport { */ bool halted; =20 + int fuse_fd; + char *mountpoint; bool writable; bool growable; @@ -71,20 +160,31 @@ typedef struct FuseExport { gid_t st_gid; } FuseExport; =20 +/* + * Verify that the size of FuseRequestInHeaderBuf.head plus the data + * buffer are big enough to be accepted by the FUSE kernel driver. + */ +QEMU_BUILD_BUG_ON(sizeof(((FuseRequestInHeaderBuf *)0)->head) + + FUSE_MAX_WRITE_BYTES < + FUSE_MIN_READ_BUFFER); + static GHashTable *exports; -static const struct fuse_lowlevel_ops fuse_ops; =20 static void fuse_export_shutdown(BlockExport *exp); static void fuse_export_delete(BlockExport *exp); -static void fuse_export_halt(FuseExport *exp) G_GNUC_UNUSED; +static void fuse_export_halt(FuseExport *exp); =20 static void init_exports_table(void); =20 static int mount_fuse_export(FuseExport *exp, Error **errp); -static void read_from_fuse_export(void *opaque); =20 static bool is_regular_file(const char *path, Error **errp); =20 +static void read_from_fuse_fd(void *opaque); +static void fuse_process_request(FuseExport *exp, + const FuseRequestInHeader *in_hdr, + const void *data_buffer); +static int fuse_write_err(int fd, const struct fuse_in_header *in_hdr, int= err); =20 static void fuse_inc_in_flight(FuseExport *exp) { @@ -105,22 +205,26 @@ static void fuse_dec_in_flight(FuseExport *exp) } } =20 +/** + * Attach FUSE FD read handler. + */ static void fuse_attach_handlers(FuseExport *exp) { if (qatomic_read(&exp->halted)) { return; } =20 - aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), - read_from_fuse_export, NULL, NULL, NULL, exp); + aio_set_fd_handler(exp->common.ctx, exp->fuse_fd, + read_from_fuse_fd, NULL, NULL, NULL, exp); exp->fd_handler_set_up =3D true; } =20 +/** + * Detach FUSE FD read handler. + */ static void fuse_detach_handlers(FuseExport *exp) { - aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), + aio_set_fd_handler(exp->common.ctx, exp->fuse_fd, NULL, NULL, NULL, NULL, NULL); exp->fd_handler_set_up =3D false; } @@ -247,6 +351,13 @@ static int fuse_export_create(BlockExport *blk_exp, =20 g_hash_table_insert(exports, g_strdup(exp->mountpoint), NULL); =20 + exp->fuse_fd =3D fuse_session_fd(exp->fuse_session); + ret =3D qemu_fcntl_addfl(exp->fuse_fd, O_NONBLOCK); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to make FUSE FD non-blocking"= ); + goto fail; + } + fuse_attach_handlers(exp); return 0; =20 @@ -278,6 +389,17 @@ static int mount_fuse_export(FuseExport *exp, Error **= errp) char *mount_opts; struct fuse_args fuse_args; int ret; + /* + * We just create the session for mounting/unmounting, no need to prov= ide + * any operations. However, since libfuse commit 52a633a5d, we have to + * provide some op struct and cannot just pass NULL (even though the c= ommit + * message ("allow passing ops as NULL") seems to imply the exact oppo= site, + * as does the comment added to fuse_session_new_fn() ("To create a no= -op + * session just for mounting pass op as NULL."). + * This is how said libfuse commit implements a no-op session internal= ly, so + * do it the same way. + */ + static const struct fuse_lowlevel_ops null_ops =3D { 0 }; =20 /* * Note that these mount options differ from what we would pass to a d= irect @@ -292,7 +414,7 @@ static int mount_fuse_export(FuseExport *exp, Error **e= rrp) mount_opts =3D g_strdup_printf("%s,nosuid,nodev,noatime,max_read=3D%zu= ," "default_permissions%s", exp->writable ? "rw" : "ro", - FUSE_MAX_BOUNCE_BYTES, + FUSE_MAX_READ_BYTES, exp->allow_other ? ",allow_other" : ""); =20 fuse_argv[0] =3D ""; /* Dummy program name */ @@ -301,8 +423,8 @@ static int mount_fuse_export(FuseExport *exp, Error **e= rrp) fuse_argv[3] =3D NULL; fuse_args =3D (struct fuse_args)FUSE_ARGS_INIT(3, (char **)fuse_argv); =20 - exp->fuse_session =3D fuse_session_new(&fuse_args, &fuse_ops, - sizeof(fuse_ops), exp); + exp->fuse_session =3D fuse_session_new(&fuse_args, &null_ops, + sizeof(null_ops), NULL); g_free(mount_opts); if (!exp->fuse_session) { error_setg(errp, "Failed to set up FUSE session"); @@ -326,36 +448,163 @@ fail: } =20 /** - * Callback to be invoked when the FUSE session FD can be read from. - * (This is basically the FUSE event loop.) + * Allocate a buffer to receive WRITE data, or take the cached one. */ -static void read_from_fuse_export(void *opaque) +static void *get_write_data_buffer(FuseExport *exp) { - FuseExport *exp =3D opaque; - int ret; + if (exp->req_write_data_cached) { + void *cached =3D exp->req_write_data_cached; + exp->req_write_data_cached =3D NULL; + return cached; + } else { + return blk_blockalign(exp->common.blk, FUSE_MAX_WRITE_BYTES); + } +} =20 - if (unlikely(qatomic_read(&exp->halted))) { +/** + * Release a WRITE data buffer, possibly reusing it for a subsequent reque= st. + */ +static void release_write_data_buffer(FuseExport *exp, void **buffer) +{ + if (!*buffer) { return; } =20 + if (!exp->req_write_data_cached) { + exp->req_write_data_cached =3D *buffer; + } else { + qemu_vfree(*buffer); + } + *buffer =3D NULL; +} + +/** + * Return the length of the specific operation's own in_header. + * Return -ENOSYS if the operation is not supported. + */ +static ssize_t req_op_hdr_len(const FuseRequestInHeader *in_hdr) +{ + switch (in_hdr->common.opcode) { + case FUSE_INIT: + return sizeof(in_hdr->init); + case FUSE_OPEN: + return sizeof(in_hdr->open); + case FUSE_SETATTR: + return sizeof(in_hdr->setattr); + case FUSE_READ: + return sizeof(in_hdr->read); + case FUSE_WRITE: + return sizeof(in_hdr->write); + case FUSE_FALLOCATE: + return sizeof(in_hdr->fallocate); +#ifdef CONFIG_FUSE_LSEEK + case FUSE_LSEEK: + return sizeof(in_hdr->lseek); +#endif + case FUSE_DESTROY: + case FUSE_STATFS: + case FUSE_RELEASE: + case FUSE_LOOKUP: + case FUSE_FORGET: + case FUSE_BATCH_FORGET: + case FUSE_GETATTR: + case FUSE_FSYNC: + case FUSE_FLUSH: + /* These requests don't have their own header or we don't care */ + return 0; + default: + return -ENOSYS; + } +} + +/** + * Try to read and process a single request from the FUSE FD. + */ +static void read_from_fuse_fd(void *opaque) +{ + FuseExport *exp =3D opaque; + int fuse_fd =3D exp->fuse_fd; + ssize_t ret; + FuseRequestInHeaderBuf in_hdr_buf; + const FuseRequestInHeader *in_hdr; + void *data_buffer =3D NULL; + struct iovec iov[2]; + ssize_t op_hdr_len; + fuse_inc_in_flight(exp); =20 - do { - ret =3D fuse_session_receive_buf(exp->fuse_session, &exp->fuse_buf= ); - } while (ret =3D=3D -EINTR); - if (ret < 0) { - goto out; + if (unlikely(qatomic_read(&exp->halted))) { + goto no_request; + } + + data_buffer =3D get_write_data_buffer(exp); + + /* Construct the I/O vector to hold the FUSE request */ + iov[0] =3D (struct iovec) { &in_hdr_buf.head, sizeof(in_hdr_buf.head) = }; + iov[1] =3D (struct iovec) { data_buffer, FUSE_MAX_WRITE_BYTES }; + ret =3D RETRY_ON_EINTR(readv(fuse_fd, iov, ARRAY_SIZE(iov))); + if (ret < 0 && errno =3D=3D EAGAIN) { + /* No request available */ + goto no_request; + } else if (unlikely(ret < 0)) { + error_report("Failed to read from FUSE device: %s", strerror(errno= )); + goto no_request; + } + + if (unlikely(ret < sizeof(in_hdr->common))) { + error_report("Incomplete read from FUSE device, expected at least = %zu " + "bytes, read %zi bytes; cannot trust subsequent " + "requests, halting the export", + sizeof(in_hdr->common), ret); + fuse_export_halt(exp); + goto no_request; + } + in_hdr =3D &in_hdr_buf.structured; + + if (unlikely(ret !=3D in_hdr->common.len)) { + error_report("Number of bytes read from FUSE device does not match= " + "request size, expected %" PRIu32 " bytes, read %zi " + "bytes; cannot trust subsequent requests, halting the= " + "export", + in_hdr->common.len, ret); + fuse_export_halt(exp); + goto no_request; + } + + op_hdr_len =3D req_op_hdr_len(in_hdr); + if (op_hdr_len < 0) { + fuse_write_err(fuse_fd, &in_hdr->common, op_hdr_len); + goto no_request; + } + + if (unlikely(ret < sizeof(in_hdr->common) + op_hdr_len)) { + error_report("FUSE request truncated, expected %zu bytes, read %zi= " + "bytes", + sizeof(in_hdr->common) + op_hdr_len, ret); + fuse_write_err(fuse_fd, &in_hdr->common, -EINVAL); + goto no_request; } =20 /* - * Note that aio_poll() in any request-processing function can lead to= a - * nested read_from_fuse_export() call, which will overwrite the conte= nts of - * exp->fuse_buf. Anything that takes a buffer needs to take care tha= t the - * content is copied before potentially polling via aio_poll(). + * Only WRITE uses the write data buffer, so for non-WRITE requests lo= nger + * than .head, we need to copy any data that spilled into data_buffer = into + * .tail. Then we can release the write data buffer. */ - fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); + if (in_hdr->common.opcode !=3D FUSE_WRITE) { + if (ret > sizeof(in_hdr_buf.head)) { + size_t len; + /* Limit size to prevent overflow */ + len =3D MIN(ret - sizeof(in_hdr_buf.head), sizeof(in_hdr_buf.t= ail)); + memcpy(in_hdr_buf.tail, data_buffer, len); + } + + release_write_data_buffer(exp, &data_buffer); + } =20 -out: + fuse_process_request(exp, in_hdr, data_buffer); + +no_request: + release_write_data_buffer(exp, &data_buffer); fuse_dec_in_flight(exp); } =20 @@ -363,18 +612,14 @@ static void fuse_export_shutdown(BlockExport *blk_exp) { FuseExport *exp =3D container_of(blk_exp, FuseExport, common); =20 - if (exp->fuse_session) { - fuse_session_exit(exp->fuse_session); - - if (exp->fd_handler_set_up) { - fuse_detach_handlers(exp); - } + if (exp->fd_handler_set_up) { + fuse_detach_handlers(exp); } =20 if (exp->mountpoint) { /* - * Safe to drop now, because we will not handle any requests - * for this export anymore anyway. + * Safe to drop now, because we will not handle any requests for t= his + * export anymore anyway (at least not from the main thread). */ g_hash_table_remove(exports, exp->mountpoint); } @@ -392,7 +637,7 @@ static void fuse_export_delete(BlockExport *blk_exp) fuse_session_destroy(exp->fuse_session); } =20 - free(exp->fuse_buf.mem); + qemu_vfree(exp->req_write_data_cached); g_free(exp->mountpoint); } =20 @@ -434,46 +679,101 @@ static bool is_regular_file(const char *path, Error = **errp) } =20 /** - * A chance to set change some parameters supplied to FUSE_INIT. + * Process FUSE INIT. + * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static void fuse_init(void *userdata, struct fuse_conn_info *conn) +static ssize_t fuse_init(FuseExport *exp, struct fuse_init_out *out, + const struct fuse_init_in_compat *in) { + const uint32_t supported_flags =3D FUSE_ASYNC_READ | FUSE_ASYNC_DIO; + + if (in->major !=3D 7) { + error_report("FUSE major version mismatch: We have 7, but kernel h= as %" + PRIu32, in->major); + return -EINVAL; + } + + /* 2007's 7.9 added fuse_attr.blksize; working around that would be ha= rd */ + if (in->minor < 9) { + error_report("FUSE minor version too old: 9 required, but kernel h= as %" + PRIu32, in->minor); + return -EINVAL; + } + + *out =3D (struct fuse_init_out) { + .major =3D 7, + .minor =3D MIN(FUSE_KERNEL_MINOR_VERSION, in->minor), + .max_readahead =3D in->max_readahead, + .max_write =3D FUSE_MAX_WRITE_BYTES, + .flags =3D in->flags & supported_flags, + .flags2 =3D 0, + + /* libfuse maximum: 2^16 - 1 */ + .max_background =3D UINT16_MAX, + + /* libfuse default: max_background * 3 / 4 */ + .congestion_threshold =3D (int)UINT16_MAX * 3 / 4, + + /* libfuse default: 1 */ + .time_gran =3D 1, + + /* + * probably unneeded without FUSE_MAX_PAGES, but this would be the + * libfuse default + */ + .max_pages =3D DIV_ROUND_UP(FUSE_MAX_WRITE_BYTES, + qemu_real_host_page_size()), + + /* Only needed for mappings (i.e. DAX) */ + .map_alignment =3D 0, + }; + /* - * MIN_NON_ZERO() would not be wrong here, but what we set here - * must equal what has been passed to fuse_session_new(). - * Therefore, as long as max_read must be passed as a mount option - * (which libfuse claims will be changed at some point), we have - * to set max_read to a fixed value here. + * Before 7.23, fuse_init_out is shorter. + * Drop the tail (time_gran, max_pages, map_alignment). */ - conn->max_read =3D FUSE_MAX_BOUNCE_BYTES; - - conn->max_write =3D MIN_NON_ZERO(BDRV_REQUEST_MAX_BYTES, conn->max_wri= te); + return out->minor >=3D 23 ? sizeof(*out) : FUSE_COMPAT_22_INIT_OUT_SIZ= E; } =20 /** - * Let clients look up files. Always return ENOENT because we only - * care about the mountpoint itself. + * Return some filesystem information, just to not break e.g. `df`. */ -static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *nam= e) +static ssize_t fuse_statfs(FuseExport *exp, struct fuse_statfs_out *out) { - fuse_reply_err(req, ENOENT); + BlockDriverState *root_bs; + uint32_t opt_transfer =3D 512; + + root_bs =3D blk_bs(exp->common.blk); + if (root_bs) { + opt_transfer =3D root_bs->bl.opt_transfer; + if (!opt_transfer) { + opt_transfer =3D root_bs->bl.request_alignment; + } + opt_transfer =3D MAX(opt_transfer, 512); + } + + *out =3D (struct fuse_statfs_out) { + /* These are the fields libfuse sets by default */ + .st =3D { + .namelen =3D 255, + .bsize =3D opt_transfer, + }, + }; + return sizeof(*out); } =20 /** * Let clients get file attributes (i.e., stat() the file). + * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static void fuse_getattr(fuse_req_t req, fuse_ino_t inode, - struct fuse_file_info *fi) +static ssize_t fuse_getattr(FuseExport *exp, struct fuse_attr_out *out) { - struct stat statbuf; int64_t length, allocated_blocks; time_t now =3D time(NULL); - FuseExport *exp =3D fuse_req_userdata(req); =20 length =3D blk_getlength(exp->common.blk); if (length < 0) { - fuse_reply_err(req, -length); - return; + return length; } =20 allocated_blocks =3D bdrv_get_allocated_file_size(blk_bs(exp->common.b= lk)); @@ -483,21 +783,24 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t i= node, allocated_blocks =3D DIV_ROUND_UP(allocated_blocks, 512); } =20 - statbuf =3D (struct stat) { - .st_ino =3D 1, - .st_mode =3D exp->st_mode, - .st_nlink =3D 1, - .st_uid =3D exp->st_uid, - .st_gid =3D exp->st_gid, - .st_size =3D length, - .st_blksize =3D blk_bs(exp->common.blk)->bl.request_alignment, - .st_blocks =3D allocated_blocks, - .st_atime =3D now, - .st_mtime =3D now, - .st_ctime =3D now, + *out =3D (struct fuse_attr_out) { + .attr_valid =3D 1, + .attr =3D { + .ino =3D 1, + .mode =3D exp->st_mode, + .nlink =3D 1, + .uid =3D exp->st_uid, + .gid =3D exp->st_gid, + .size =3D length, + .blksize =3D blk_bs(exp->common.blk)->bl.request_alignment, + .blocks =3D allocated_blocks, + .atime =3D now, + .mtime =3D now, + .ctime =3D now, + }, }; =20 - fuse_reply_attr(req, &statbuf, 1.); + return sizeof(*out); } =20 static int fuse_do_truncate(const FuseExport *exp, int64_t size, @@ -520,101 +823,99 @@ static int fuse_do_truncate(const FuseExport *exp, i= nt64_t size, * permit access: Read-only exports cannot be given +w, and exports * without allow_other cannot be given a different UID or GID, and * they cannot be given non-owner access. + * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *st= atbuf, - int to_set, struct fuse_file_info *fi) +static ssize_t fuse_setattr(FuseExport *exp, struct fuse_attr_out *out, + uint32_t to_set, uint64_t size, uint32_t mode, + uint32_t uid, uint32_t gid) { - FuseExport *exp =3D fuse_req_userdata(req); int supported_attrs; int ret; =20 - supported_attrs =3D FUSE_SET_ATTR_SIZE | FUSE_SET_ATTR_MODE; + /* SIZE and MODE are actually supported, the others can be safely igno= red */ + supported_attrs =3D FATTR_SIZE | FATTR_MODE | + FATTR_FH | FATTR_LOCKOWNER | FATTR_KILL_SUIDGID; if (exp->allow_other) { - supported_attrs |=3D FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID; + supported_attrs |=3D FATTR_UID | FATTR_GID; } =20 if (to_set & ~supported_attrs) { - fuse_reply_err(req, ENOTSUP); - return; + return -ENOTSUP; } =20 /* Do some argument checks first before committing to anything */ - if (to_set & FUSE_SET_ATTR_MODE) { + if (to_set & FATTR_MODE) { /* * Without allow_other, non-owners can never access the export, so= do * not allow setting permissions for them */ - if (!exp->allow_other && - (statbuf->st_mode & (S_IRWXG | S_IRWXO)) !=3D 0) - { - fuse_reply_err(req, EPERM); - return; + if (!exp->allow_other && (mode & (S_IRWXG | S_IRWXO)) !=3D 0) { + return -EPERM; } =20 /* +w for read-only exports makes no sense, disallow it */ - if (!exp->writable && - (statbuf->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) !=3D 0) - { - fuse_reply_err(req, EROFS); - return; + if (!exp->writable && (mode & (S_IWUSR | S_IWGRP | S_IWOTH)) !=3D = 0) { + return -EROFS; } } =20 - if (to_set & FUSE_SET_ATTR_SIZE) { + if (to_set & FATTR_SIZE) { if (!exp->writable) { - fuse_reply_err(req, EACCES); - return; + return -EACCES; } =20 - ret =3D fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MOD= E_OFF); + ret =3D fuse_do_truncate(exp, size, true, PREALLOC_MODE_OFF); if (ret < 0) { - fuse_reply_err(req, -ret); - return; + return ret; } } =20 - if (to_set & FUSE_SET_ATTR_MODE) { + if (to_set & FATTR_MODE) { /* Ignore FUSE-supplied file type, only change the mode */ - exp->st_mode =3D (statbuf->st_mode & 07777) | S_IFREG; + exp->st_mode =3D (mode & 07777) | S_IFREG; } =20 - if (to_set & FUSE_SET_ATTR_UID) { - exp->st_uid =3D statbuf->st_uid; + if (to_set & FATTR_UID) { + exp->st_uid =3D uid; } =20 - if (to_set & FUSE_SET_ATTR_GID) { - exp->st_gid =3D statbuf->st_gid; + if (to_set & FATTR_GID) { + exp->st_gid =3D gid; } =20 - fuse_getattr(req, inode, fi); + return fuse_getattr(exp, out); } =20 /** - * Let clients open a file (i.e., the exported image). + * Open an inode. We only have a single inode in our exported filesystem,= so we + * just acknowledge the request. + * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static void fuse_open(fuse_req_t req, fuse_ino_t inode, - struct fuse_file_info *fi) +static ssize_t fuse_open(FuseExport *exp, struct fuse_open_out *out) { - fi->direct_io =3D true; - fi->parallel_direct_writes =3D true; - fuse_reply_open(req, fi); + *out =3D (struct fuse_open_out) { + .open_flags =3D FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES, + }; + return sizeof(*out); } =20 /** - * Handle client reads from the exported image. + * Handle client reads from the exported image. Allocates *bufptr and rea= ds + * data from the block device into that buffer. + * Returns the buffer (read) size on success, and -errno on error. + * Note: If the returned size is 0, *bufptr will be set to NULL. + * After use, *bufptr must be freed via qemu_vfree(). */ -static void fuse_read(fuse_req_t req, fuse_ino_t inode, - size_t size, off_t offset, struct fuse_file_info *fi) +static ssize_t fuse_read(FuseExport *exp, void **bufptr, + uint64_t offset, uint32_t size) { - FuseExport *exp =3D fuse_req_userdata(req); int64_t blk_len; void *buf; int ret; =20 /* Limited by max_read, should not happen */ - if (size > FUSE_MAX_BOUNCE_BYTES) { - fuse_reply_err(req, EINVAL); - return; + if (size > FUSE_MAX_READ_BYTES) { + return -EINVAL; } =20 /** @@ -623,18 +924,13 @@ static void fuse_read(fuse_req_t req, fuse_ino_t inod= e, */ blk_len =3D blk_getlength(exp->common.blk); if (blk_len < 0) { - fuse_reply_err(req, -blk_len); - return; + return blk_len; } =20 if (offset >=3D blk_len) { - /* - * Technically libfuse does not allow returning a zero error code = for - * read requests, but in practice this is a 0-length read (and a f= uture - * commit will change this code anyway) - */ - fuse_reply_err(req, 0); - return; + /* Explicitly set to NULL because we return success here */ + *bufptr =3D NULL; + return 0; } =20 if (offset + size > blk_len) { @@ -643,108 +939,96 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino= de, =20 buf =3D qemu_try_blockalign(blk_bs(exp->common.blk), size); if (!buf) { - fuse_reply_err(req, ENOMEM); - return; + return -ENOMEM; } =20 ret =3D blk_pread(exp->common.blk, offset, size, buf, 0); - if (ret >=3D 0) { - fuse_reply_buf(req, buf, size); - } else { - fuse_reply_err(req, -ret); + if (ret < 0) { + qemu_vfree(buf); + return ret; } =20 - qemu_vfree(buf); + *bufptr =3D buf; + return size; } =20 /** - * Handle client writes to the exported image. + * Handle client writes to the exported image. @buf has the data to be wr= itten. + * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, - size_t size, off_t offset, struct fuse_file_info *f= i) +static ssize_t fuse_write(FuseExport *exp, struct fuse_write_out *out, + uint64_t offset, uint32_t size, const void *buf) { - FuseExport *exp =3D fuse_req_userdata(req); - QEMU_AUTO_VFREE void *copied =3D NULL; int64_t blk_len; int ret; =20 + QEMU_BUILD_BUG_ON(FUSE_MAX_WRITE_BYTES > BDRV_REQUEST_MAX_BYTES); /* Limited by max_write, should not happen */ - if (size > BDRV_REQUEST_MAX_BYTES) { - fuse_reply_err(req, EINVAL); - return; + if (size > FUSE_MAX_WRITE_BYTES) { + return -EINVAL; } =20 if (!exp->writable) { - fuse_reply_err(req, EACCES); - return; + return -EACCES; } =20 - /* - * Heed the note on read_from_fuse_export(): If we call aio_poll() (wh= ich - * any blk_*() I/O function may do), read_from_fuse_export() may be ne= sted, - * overwriting the request buffer content. Therefore, we must copy it= here. - */ - copied =3D blk_blockalign(exp->common.blk, size); - memcpy(copied, buf, size); - /** * Clients will expect short writes at EOF, so we have to limit * offset+size to the image length. */ blk_len =3D blk_getlength(exp->common.blk); if (blk_len < 0) { - fuse_reply_err(req, -blk_len); - return; + return blk_len; } =20 if (offset >=3D blk_len && !exp->growable) { - fuse_reply_write(req, 0); - return; + *out =3D (struct fuse_write_out) { + .size =3D 0, + }; + return sizeof(*out); } =20 if (offset + size < offset) { - fuse_reply_err(req, EINVAL); - return; + return -EINVAL; } else if (offset + size > blk_len) { if (exp->growable) { ret =3D fuse_do_truncate(exp, offset + size, true, PREALLOC_MO= DE_OFF); if (ret < 0) { - fuse_reply_err(req, -ret); - return; + return ret; } } else { size =3D blk_len - offset; } } =20 - ret =3D blk_pwrite(exp->common.blk, offset, size, copied, 0); - if (ret >=3D 0) { - fuse_reply_write(req, size); - } else { - fuse_reply_err(req, -ret); + ret =3D blk_pwrite(exp->common.blk, offset, size, buf, 0); + if (ret < 0) { + return ret; } + + *out =3D (struct fuse_write_out) { + .size =3D size, + }; + return sizeof(*out); } =20 /** * Let clients perform various fallocate() operations. + * Return 0 on success (no 'out' object), and -errno on error. */ -static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, - off_t offset, off_t length, - struct fuse_file_info *fi) +static ssize_t fuse_fallocate(FuseExport *exp, uint64_t offset, uint64_t l= ength, + uint32_t mode) { - FuseExport *exp =3D fuse_req_userdata(req); int64_t blk_len; int ret; =20 if (!exp->writable) { - fuse_reply_err(req, EACCES); - return; + return -EACCES; } =20 blk_len =3D blk_getlength(exp->common.blk); if (blk_len < 0) { - fuse_reply_err(req, -blk_len); - return; + return blk_len; } =20 #ifdef CONFIG_FALLOCATE_PUNCH_HOLE @@ -756,16 +1040,14 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_= t inode, int mode, if (!mode) { /* We can only fallocate at the EOF with a truncate */ if (offset < blk_len) { - fuse_reply_err(req, EOPNOTSUPP); - return; + return -EOPNOTSUPP; } =20 if (offset > blk_len) { /* No preallocation needed here */ ret =3D fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF); if (ret < 0) { - fuse_reply_err(req, -ret); - return; + return ret; } } =20 @@ -775,8 +1057,7 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t = inode, int mode, #ifdef CONFIG_FALLOCATE_PUNCH_HOLE else if (mode & FALLOC_FL_PUNCH_HOLE) { if (!(mode & FALLOC_FL_KEEP_SIZE)) { - fuse_reply_err(req, EINVAL); - return; + return -EINVAL; } =20 do { @@ -804,8 +1085,7 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t = inode, int mode, ret =3D fuse_do_truncate(exp, offset + length, false, PREALLOC_MODE_OFF); if (ret < 0) { - fuse_reply_err(req, -ret); - return; + return ret; } } =20 @@ -823,44 +1103,38 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_= t inode, int mode, ret =3D -EOPNOTSUPP; } =20 - fuse_reply_err(req, ret < 0 ? -ret : 0); + return ret < 0 ? ret : 0; } =20 /** * Let clients fsync the exported image. + * Return 0 on success (no 'out' object), and -errno on error. */ -static void fuse_fsync(fuse_req_t req, fuse_ino_t inode, int datasync, - struct fuse_file_info *fi) +static ssize_t fuse_fsync(FuseExport *exp) { - FuseExport *exp =3D fuse_req_userdata(req); - int ret; - - ret =3D blk_flush(exp->common.blk); - fuse_reply_err(req, ret < 0 ? -ret : 0); + return blk_flush(exp->common.blk); } =20 /** * Called before an FD to the exported image is closed. (libfuse * notes this to be a way to return last-minute errors.) + * Return 0 on success (no 'out' object), and -errno on error. */ -static void fuse_flush(fuse_req_t req, fuse_ino_t inode, - struct fuse_file_info *fi) +static ssize_t fuse_flush(FuseExport *exp) { - fuse_fsync(req, inode, 1, fi); + return blk_flush(exp->common.blk); } =20 #ifdef CONFIG_FUSE_LSEEK /** * Let clients inquire allocation status. + * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static void fuse_lseek(fuse_req_t req, fuse_ino_t inode, off_t offset, - int whence, struct fuse_file_info *fi) +static ssize_t fuse_lseek(FuseExport *exp, struct fuse_lseek_out *out, + uint64_t offset, uint32_t whence) { - FuseExport *exp =3D fuse_req_userdata(req); - if (whence !=3D SEEK_HOLE && whence !=3D SEEK_DATA) { - fuse_reply_err(req, EINVAL); - return; + return -EINVAL; } =20 while (true) { @@ -870,8 +1144,7 @@ static void fuse_lseek(fuse_req_t req, fuse_ino_t inod= e, off_t offset, ret =3D bdrv_block_status_above(blk_bs(exp->common.blk), NULL, offset, INT64_MAX, &pnum, NULL, NULL= ); if (ret < 0) { - fuse_reply_err(req, -ret); - return; + return ret; } =20 if (!pnum && (ret & BDRV_BLOCK_EOF)) { @@ -888,34 +1161,38 @@ static void fuse_lseek(fuse_req_t req, fuse_ino_t in= ode, off_t offset, =20 blk_len =3D blk_getlength(exp->common.blk); if (blk_len < 0) { - fuse_reply_err(req, -blk_len); - return; + return blk_len; } =20 if (offset > blk_len || whence =3D=3D SEEK_DATA) { - fuse_reply_err(req, ENXIO); - } else { - fuse_reply_lseek(req, offset); + return -ENXIO; } - return; + + *out =3D (struct fuse_lseek_out) { + .offset =3D offset, + }; + return sizeof(*out); } =20 if (ret & BDRV_BLOCK_DATA) { if (whence =3D=3D SEEK_DATA) { - fuse_reply_lseek(req, offset); - return; + *out =3D (struct fuse_lseek_out) { + .offset =3D offset, + }; + return sizeof(*out); } } else { if (whence =3D=3D SEEK_HOLE) { - fuse_reply_lseek(req, offset); - return; + *out =3D (struct fuse_lseek_out) { + .offset =3D offset, + }; + return sizeof(*out); } } =20 /* Safety check against infinite loops */ if (!pnum) { - fuse_reply_err(req, ENXIO); - return; + return -ENXIO; } =20 offset +=3D pnum; @@ -923,21 +1200,241 @@ static void fuse_lseek(fuse_req_t req, fuse_ino_t i= node, off_t offset, } #endif =20 -static const struct fuse_lowlevel_ops fuse_ops =3D { - .init =3D fuse_init, - .lookup =3D fuse_lookup, - .getattr =3D fuse_getattr, - .setattr =3D fuse_setattr, - .open =3D fuse_open, - .read =3D fuse_read, - .write =3D fuse_write, - .fallocate =3D fuse_fallocate, - .flush =3D fuse_flush, - .fsync =3D fuse_fsync, +/** + * Write a FUSE response to the given @fd. + * + * Effectively, writes out_hdr->common.len bytes of the buffer that is *ou= t_hdr. + * + * @fd: FUSE file descriptor + * @out_hdr: Request response header and request-specific response data + */ +static int fuse_write_response(int fd, FuseRequestOutHeader *out_hdr) +{ + size_t to_write =3D out_hdr->common.len; + ssize_t ret; + + /* Must at least write fuse_out_header */ + assert(to_write >=3D sizeof(out_hdr->common)); + + ret =3D RETRY_ON_EINTR(write(fd, out_hdr, to_write)); + if (ret < 0) { + ret =3D -errno; + error_report("Failed to write to FUSE device: %s", strerror(-ret)); + return ret; + } + + /* Short writes are unexpected, treat them as errors */ + if (ret !=3D to_write) { + error_report("Short write to FUSE device, wrote %zi of %zu bytes", + ret, to_write); + return -EIO; + } + + return 0; +} + +/** + * Write a FUSE error response to @fd. + * + * @fd: FUSE file descriptor + * @in_hdr: Incoming request header to which to respond + * @err: Error code (-errno, must be negative!) + */ +static int fuse_write_err(int fd, const struct fuse_in_header *in_hdr, int= err) +{ + FuseRequestOutHeader out_hdr =3D { + .common =3D { + .len =3D sizeof(out_hdr.common), + /* FUSE expects negative error values */ + .error =3D err, + .unique =3D in_hdr->unique, + }, + }; + + return fuse_write_response(fd, &out_hdr); +} + +/** + * Write a FUSE response to the given @fd, using separate buffers for the + * response header and data. + * + * In contrast to fuse_write_response(), this function cannot return a full + * FuseRequestOutHeader (i.e. including request-specific response structs), + * but only FuseRequestOutHeader.common. The remaining data must be in + * *buf. + * + * (Total length must be set in out_hdr->len.) + * + * @fd: FUSE file descriptor + * @out_hdr: Request response header + * @buf: Pointer to response data + */ +static int fuse_write_buf_response(int fd, + const struct fuse_out_header *out_hdr, + const void *buf) +{ + size_t to_write =3D out_hdr->len; + struct iovec iov[2] =3D { + { (void *)out_hdr, sizeof(*out_hdr) }, + { (void *)buf, to_write - sizeof(*out_hdr) }, + }; + ssize_t ret; + + /* *buf length must not be negative */ + assert(to_write >=3D sizeof(*out_hdr)); + + ret =3D RETRY_ON_EINTR(writev(fd, iov, ARRAY_SIZE(iov))); + if (ret < 0) { + ret =3D -errno; + error_report("Failed to write to FUSE device: %s", strerror(-ret)); + return ret; + } + + /* Short writes are unexpected, treat them as errors */ + if (ret !=3D to_write) { + error_report("Short write to FUSE device, wrote %zi of %zu bytes", + ret, to_write); + return -EIO; + } + + return 0; +} + +/** + * Process a FUSE request, incl. writing the response. + */ +static void fuse_process_request(FuseExport *exp, + const FuseRequestInHeader *in_hdr, + const void *data_buffer) +{ + FuseRequestOutHeader out_hdr; + /* For read requests: Data to be returned */ + void *out_data_buffer =3D NULL; + ssize_t ret; + + switch (in_hdr->common.opcode) { + case FUSE_INIT: + ret =3D fuse_init(exp, &out_hdr.init, &in_hdr->init); + break; + + case FUSE_DESTROY: + ret =3D 0; + break; + + case FUSE_STATFS: + ret =3D fuse_statfs(exp, &out_hdr.statfs); + break; + + case FUSE_OPEN: + ret =3D fuse_open(exp, &out_hdr.open); + break; + + case FUSE_RELEASE: + ret =3D 0; + break; + + case FUSE_LOOKUP: + ret =3D -ENOENT; /* There is no node but the root node */ + break; + + case FUSE_FORGET: + case FUSE_BATCH_FORGET: + /* These have no response, and there is nothing we need to do */ + return; + + case FUSE_GETATTR: + ret =3D fuse_getattr(exp, &out_hdr.attr); + break; + + case FUSE_SETATTR: { + const struct fuse_setattr_in *in =3D &in_hdr->setattr; + ret =3D fuse_setattr(exp, &out_hdr.attr, + in->valid, in->size, in->mode, in->uid, in->gid= ); + break; + } + + case FUSE_READ: { + const struct fuse_read_in *in =3D &in_hdr->read; + ret =3D fuse_read(exp, &out_data_buffer, in->offset, in->size); + break; + } + + case FUSE_WRITE: { + const struct fuse_write_in *in =3D &in_hdr->write; + uint32_t req_len =3D in_hdr->common.len; + + if (unlikely(req_len < sizeof(in_hdr->common) + sizeof(*in) + + in->size)) { + warn_report("FUSE WRITE truncated; received %zu bytes of %" PR= Iu32, + req_len - sizeof(in_hdr->common) - sizeof(*in), + in->size); + ret =3D -EINVAL; + break; + } + + /* + * read_from_fuse_fd() has checked that in_hdr->len matches the nu= mber + * of bytes read, which cannot exceed the max_write value we set + * (FUSE_MAX_WRITE_BYTES). So we know that FUSE_MAX_WRITE_BYTES >= =3D + * in_hdr->len >=3D in->size + X, so this assertion must hold. + */ + assert(in->size <=3D FUSE_MAX_WRITE_BYTES); + + ret =3D fuse_write(exp, &out_hdr.write, + in->offset, in->size, data_buffer); + break; + } + + case FUSE_FALLOCATE: { + const struct fuse_fallocate_in *in =3D &in_hdr->fallocate; + ret =3D fuse_fallocate(exp, in->offset, in->length, in->mode); + break; + } + + case FUSE_FSYNC: + ret =3D fuse_fsync(exp); + break; + + case FUSE_FLUSH: + ret =3D fuse_flush(exp); + break; + #ifdef CONFIG_FUSE_LSEEK - .lseek =3D fuse_lseek, + case FUSE_LSEEK: { + const struct fuse_lseek_in *in =3D &in_hdr->lseek; + ret =3D fuse_lseek(exp, &out_hdr.lseek, in->offset, in->whence); + break; + } #endif -}; + + default: + ret =3D -ENOSYS; + } + + if (ret >=3D 0) { + out_hdr.common =3D (struct fuse_out_header) { + .len =3D sizeof(out_hdr.common) + ret, + .unique =3D in_hdr->common.unique, + }; + } else { + /* fuse_read() must not return a buffer in case of error */ + assert(out_data_buffer =3D=3D NULL); + + out_hdr.common =3D (struct fuse_out_header) { + .len =3D sizeof(out_hdr.common), + /* FUSE expects negative errno values */ + .error =3D ret, + .unique =3D in_hdr->common.unique, + }; + } + + if (out_data_buffer) { + fuse_write_buf_response(exp->fuse_fd, &out_hdr.common, out_data_bu= ffer); + qemu_vfree(out_data_buffer); + } else { + fuse_write_response(exp->fuse_fd, &out_hdr); + } +} =20 const BlockExportDriver blk_exp_fuse =3D { .type =3D BLOCK_EXPORT_TYPE_FUSE, --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160168; cv=none; d=zohomail.com; s=zohoarc; b=UbWBwLpHPItZj8j6D8HACmzTwq9Cp8oarnoJc3lq5DVeSCsIyB6tnqMQdUHDADineqSzyh0LzAZgSUNOwBJHerRx5AOCq/1IkJkNzrfK3xAXSQJf6ZFIkTe7agWMyYvVrSusd05r+IRwBlABq2LP4N7mbXszuIgITk96Aarg35Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160168; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=j1zNq0gkwMJJa9HJNegRdCHxMB46SL9Pee5peNOWcGg=; b=lUEIcbGaNUYNsQCf+2LPFMz+hTtrnPJ34bwj/u7NTp3qooFOeVfA67vjnVMn0bwH5XMK3ToIFSRtJDnh6K0QW3XY8+xPB8MvE+CM+S9h8Am5sSFn7Q6Qq+zfk2/TF9CsHxiq2i4aaTihWqpVkZE2Wr/wBAusvFs/hOM6C6iF2Lw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160168673172.77469302678003; Tue, 10 Mar 2026 09:29:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvV-0003c1-E3; Tue, 10 Mar 2026 12:27:21 -0400 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 1vzzvF-0003PE-TS for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:06 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvD-0000w7-6S for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:05 -0400 Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-17-V7dINj91MX6NnbDt8k3BxA-1; Tue, 10 Mar 2026 12:27:01 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C914618005B6; Tue, 10 Mar 2026 16:26:59 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8E68C19560A6; Tue, 10 Mar 2026 16:26:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160022; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=j1zNq0gkwMJJa9HJNegRdCHxMB46SL9Pee5peNOWcGg=; b=H4IUpRqMrpDNZZwkEO0u67TQMFUkevE5H2mb+5r520wrN7CcxMpZERCFwWLQkFeT4c94s8 KFTZkfRKS4lURUrt45ei+HpP/mQvyS2X2kg5a0AWejAAPPfdgvsdfybbCdineW8s8//mQM UezhbwBtMlyiGXy0aN5d5XEuv2gvtAA= X-MC-Unique: V7dINj91MX6NnbDt8k3BxA-1 X-Mimecast-MFC-AGG-ID: V7dINj91MX6NnbDt8k3BxA_1773160019 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 18/28] fuse: Reduce max read size Date: Tue, 10 Mar 2026 17:26:12 +0100 Message-ID: <20260310162622.333137-19-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160171248158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek We are going to introduce parallel processing via coroutines, a maximum read size of 64 MB may be problematic, allowing users of the export to force us to allocate quite large amounts of memory with just a few requests. At least tone it down to 1 MB, which is still probably far more than enough. (Larger requests are split automatically by the FUSE kernel driver anyway.) (Yes, we inadvertently already had parallel request processing due to nested polling before. Better to fix this late than never.) Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-19-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index bd099d12911..f32e74f39dd 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -45,7 +45,7 @@ #endif =20 /* Prevent overly long bounce buffer allocations */ -#define FUSE_MAX_READ_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 64 * 1024 * 1024)) +#define FUSE_MAX_READ_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 1 * 1024 * 1024)) #define FUSE_MAX_WRITE_BYTES (64 * 1024) =20 /* --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160190; cv=none; d=zohomail.com; s=zohoarc; b=OMQA3xZBUIlH7j0ED+LFgtigF7kK6kQ3JO01P3ZCC74ek0cLMOsnalfqsCw6udu7/GLXf/LFBb49aY64p+KFNI6WI1xRDB/FMcs+Y2lCDZf8B9VHuufVXWlI6br1EJJoe1qxcaxSLdVaGQ1fq8q6tNrgOJ1611ghadgkfNxe7M4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160190; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=LB/woqVlxsVC7/O7ZXNLjER0uo2c8Oe6KZpAiLrH3DE=; b=GWCVJ9NbJaJxuvWv0R4PYKdyZeLH+bTEzugYrD69DRAqTQaLbPPu2GI8ua1EkC5xfFxkSwz9d+dLDMmkCvWs743B0LbxeJbvWPzqBdy6jnKlOrzISCQAe6FMxMYWcXYojBcmcQzooZMgKfNyzw/ujgOY62na31uOYPSIKsVoe24= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160190429978.5167614331153; Tue, 10 Mar 2026 09:29:50 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvO-0003S9-Sz; Tue, 10 Mar 2026 12:27:15 -0400 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 1vzzvH-0003PW-Fp for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:08 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvE-0000wH-VT for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:07 -0400 Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-68-76i2CL3_PfmVr6-IARLzwg-1; Tue, 10 Mar 2026 12:27:02 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 648AC18005B8; Tue, 10 Mar 2026 16:27:01 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3FC4619560A6; Tue, 10 Mar 2026 16:27:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160023; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LB/woqVlxsVC7/O7ZXNLjER0uo2c8Oe6KZpAiLrH3DE=; b=QLaypyQR0uLEqh1He7/j/fu27TJWNikiXH9p9ZCus5ArKoNvJtcFCOWUkGFvISC5UiL65j DTASLMspCrS4sazwCu7wc9zc1bzv4E9yUZmWo2rNchgKAH20MOYp8C21p26b4w1HcUxvLr rkmsg4ZsFKTv0k8teOGQMOsUESYo3ZA= X-MC-Unique: 76i2CL3_PfmVr6-IARLzwg-1 X-Mimecast-MFC-AGG-ID: 76i2CL3_PfmVr6-IARLzwg_1773160021 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 19/28] fuse: Process requests in coroutines Date: Tue, 10 Mar 2026 17:26:13 +0100 Message-ID: <20260310162622.333137-20-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160192708154100 From: Hanna Czenczek Make fuse_process_request() a coroutine_fn (fuse_co_process_request()) and have read_from_fuse_fd() launch it inside of a newly created coroutine instead of running it synchronously. This way, we can process requests in parallel. These are the benchmark results, compared to (a) the original results with libfuse, and (b) the results after switching away from libfuse (i.e. before this patch): file: (vs. libfuse / vs. no libfuse) read: seq aio: 97.8k =C2=B11.5k (-2% / -8%) rand aio: 95.8k =C2=B13.4k (+90% / +98%) seq sync: 34.5k =C2=B11.0k (-4% / -3%) rand sync: 9.9k =C2=B10.1k (-1% / -1%) write: seq aio: 68.7k =C2=B11.3k (-5% / -10%) rand aio: 68.9k =C2=B11.1k (-2% / -10%) seq sync: 30.6k =C2=B10.9k (=C2=B10% / -3%) rand sync: 30.6k =C2=B10.6k (+1% / -1%) null: read: seq aio: 174.5k =C2=B16.8k (+11% / +8%) rand aio: 170.9k =C2=B15.7k (+8% / +3%) seq sync: 82.0k =C2=B13.3k (+2% / +2%) rand sync: 78.0k =C2=B14.0k (+1% / -1%) write: seq aio: 196.0k =C2=B12.8k (+27% / +6%) rand aio: 191.2k =C2=B17.9k (+24% / +2%) seq sync: 83.3k =C2=B14.4k (+9% / +1%) rand sync: 79.5k =C2=B14.4k (+9% / +1%) So there is not much difference, especially when compared to how it was with libfuse, except for the randread AIO case with an actual file. That improves greatly. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-20-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 176 ++++++++++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 72 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index f32e74f39dd..7c072409d83 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -27,6 +27,7 @@ #include "block/qapi.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" +#include "qemu/coroutine.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "system/block-backend.h" @@ -181,9 +182,9 @@ static int mount_fuse_export(FuseExport *exp, Error **e= rrp); static bool is_regular_file(const char *path, Error **errp); =20 static void read_from_fuse_fd(void *opaque); -static void fuse_process_request(FuseExport *exp, - const FuseRequestInHeader *in_hdr, - const void *data_buffer); +static void coroutine_fn +fuse_co_process_request(FuseExport *exp, const FuseRequestInHeader *in_hdr, + const void *data_buffer); static int fuse_write_err(int fd, const struct fuse_in_header *in_hdr, int= err); =20 static void fuse_inc_in_flight(FuseExport *exp) @@ -518,9 +519,14 @@ static ssize_t req_op_hdr_len(const FuseRequestInHeade= r *in_hdr) } =20 /** - * Try to read and process a single request from the FUSE FD. + * Try to read a single request from the FUSE FD. + * Takes a FuseExport pointer in `opaque`. + * + * Assumes the export's in-flight counter has already been incremented. + * + * If a request is available, process it. */ -static void read_from_fuse_fd(void *opaque) +static void coroutine_fn co_read_from_fuse_fd(void *opaque) { FuseExport *exp =3D opaque; int fuse_fd =3D exp->fuse_fd; @@ -531,8 +537,6 @@ static void read_from_fuse_fd(void *opaque) struct iovec iov[2]; ssize_t op_hdr_len; =20 - fuse_inc_in_flight(exp); - if (unlikely(qatomic_read(&exp->halted))) { goto no_request; } @@ -601,13 +605,29 @@ static void read_from_fuse_fd(void *opaque) release_write_data_buffer(exp, &data_buffer); } =20 - fuse_process_request(exp, in_hdr, data_buffer); + fuse_co_process_request(exp, in_hdr, data_buffer); =20 no_request: release_write_data_buffer(exp, &data_buffer); fuse_dec_in_flight(exp); } =20 +/** + * Try to read and process a single request from the FUSE FD. + * (To be used as a handler for when the FUSE FD becomes readable.) + * Takes a FuseExport pointer in `opaque`. + */ +static void read_from_fuse_fd(void *opaque) +{ + FuseExport *exp =3D opaque; + Coroutine *co; + + co =3D qemu_coroutine_create(co_read_from_fuse_fd, exp); + /* Decremented by co_read_from_fuse_fd() */ + fuse_inc_in_flight(exp); + qemu_coroutine_enter(co); +} + static void fuse_export_shutdown(BlockExport *blk_exp) { FuseExport *exp =3D container_of(blk_exp, FuseExport, common); @@ -682,8 +702,9 @@ static bool is_regular_file(const char *path, Error **e= rrp) * Process FUSE INIT. * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static ssize_t fuse_init(FuseExport *exp, struct fuse_init_out *out, - const struct fuse_init_in_compat *in) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_init(FuseExport *exp, struct fuse_init_out *out, + const struct fuse_init_in_compat *in) { const uint32_t supported_flags =3D FUSE_ASYNC_READ | FUSE_ASYNC_DIO; =20 @@ -738,7 +759,8 @@ static ssize_t fuse_init(FuseExport *exp, struct fuse_i= nit_out *out, /** * Return some filesystem information, just to not break e.g. `df`. */ -static ssize_t fuse_statfs(FuseExport *exp, struct fuse_statfs_out *out) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_statfs(FuseExport *exp, struct fuse_statfs_out *out) { BlockDriverState *root_bs; uint32_t opt_transfer =3D 512; @@ -766,17 +788,18 @@ static ssize_t fuse_statfs(FuseExport *exp, struct fu= se_statfs_out *out) * Let clients get file attributes (i.e., stat() the file). * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static ssize_t fuse_getattr(FuseExport *exp, struct fuse_attr_out *out) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_getattr(FuseExport *exp, struct fuse_attr_out *out) { int64_t length, allocated_blocks; time_t now =3D time(NULL); =20 - length =3D blk_getlength(exp->common.blk); + length =3D blk_co_getlength(exp->common.blk); if (length < 0) { return length; } =20 - allocated_blocks =3D bdrv_get_allocated_file_size(blk_bs(exp->common.b= lk)); + allocated_blocks =3D bdrv_co_get_allocated_file_size(blk_bs(exp->commo= n.blk)); if (allocated_blocks <=3D 0) { allocated_blocks =3D DIV_ROUND_UP(length, 512); } else { @@ -803,8 +826,9 @@ static ssize_t fuse_getattr(FuseExport *exp, struct fus= e_attr_out *out) return sizeof(*out); } =20 -static int fuse_do_truncate(const FuseExport *exp, int64_t size, - bool req_zero_write, PreallocMode prealloc) +static int coroutine_fn GRAPH_RDLOCK +fuse_co_do_truncate(const FuseExport *exp, int64_t size, bool req_zero_wri= te, + PreallocMode prealloc) { BdrvRequestFlags truncate_flags =3D 0; =20 @@ -812,8 +836,8 @@ static int fuse_do_truncate(const FuseExport *exp, int6= 4_t size, truncate_flags |=3D BDRV_REQ_ZERO_WRITE; } =20 - return blk_truncate(exp->common.blk, size, true, prealloc, - truncate_flags, NULL); + return blk_co_truncate(exp->common.blk, size, true, prealloc, + truncate_flags, NULL); } =20 /** @@ -825,9 +849,9 @@ static int fuse_do_truncate(const FuseExport *exp, int6= 4_t size, * they cannot be given non-owner access. * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static ssize_t fuse_setattr(FuseExport *exp, struct fuse_attr_out *out, - uint32_t to_set, uint64_t size, uint32_t mode, - uint32_t uid, uint32_t gid) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_setattr(FuseExport *exp, struct fuse_attr_out *out, uint32_t to_se= t, + uint64_t size, uint32_t mode, uint32_t uid, uint32_t gid) { int supported_attrs; int ret; @@ -864,7 +888,7 @@ static ssize_t fuse_setattr(FuseExport *exp, struct fus= e_attr_out *out, return -EACCES; } =20 - ret =3D fuse_do_truncate(exp, size, true, PREALLOC_MODE_OFF); + ret =3D fuse_co_do_truncate(exp, size, true, PREALLOC_MODE_OFF); if (ret < 0) { return ret; } @@ -883,7 +907,7 @@ static ssize_t fuse_setattr(FuseExport *exp, struct fus= e_attr_out *out, exp->st_gid =3D gid; } =20 - return fuse_getattr(exp, out); + return fuse_co_getattr(exp, out); } =20 /** @@ -891,7 +915,8 @@ static ssize_t fuse_setattr(FuseExport *exp, struct fus= e_attr_out *out, * just acknowledge the request. * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static ssize_t fuse_open(FuseExport *exp, struct fuse_open_out *out) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_open(FuseExport *exp, struct fuse_open_out *out) { *out =3D (struct fuse_open_out) { .open_flags =3D FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES, @@ -906,8 +931,8 @@ static ssize_t fuse_open(FuseExport *exp, struct fuse_o= pen_out *out) * Note: If the returned size is 0, *bufptr will be set to NULL. * After use, *bufptr must be freed via qemu_vfree(). */ -static ssize_t fuse_read(FuseExport *exp, void **bufptr, - uint64_t offset, uint32_t size) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_read(FuseExport *exp, void **bufptr, uint64_t offset, uint32_t siz= e) { int64_t blk_len; void *buf; @@ -922,7 +947,7 @@ static ssize_t fuse_read(FuseExport *exp, void **bufptr, * Clients will expect short reads at EOF, so we have to limit * offset+size to the image length. */ - blk_len =3D blk_getlength(exp->common.blk); + blk_len =3D blk_co_getlength(exp->common.blk); if (blk_len < 0) { return blk_len; } @@ -942,7 +967,7 @@ static ssize_t fuse_read(FuseExport *exp, void **bufptr, return -ENOMEM; } =20 - ret =3D blk_pread(exp->common.blk, offset, size, buf, 0); + ret =3D blk_co_pread(exp->common.blk, offset, size, buf, 0); if (ret < 0) { qemu_vfree(buf); return ret; @@ -956,8 +981,9 @@ static ssize_t fuse_read(FuseExport *exp, void **bufptr, * Handle client writes to the exported image. @buf has the data to be wr= itten. * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static ssize_t fuse_write(FuseExport *exp, struct fuse_write_out *out, - uint64_t offset, uint32_t size, const void *buf) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_write(FuseExport *exp, struct fuse_write_out *out, + uint64_t offset, uint32_t size, const void *buf) { int64_t blk_len; int ret; @@ -976,7 +1002,7 @@ static ssize_t fuse_write(FuseExport *exp, struct fuse= _write_out *out, * Clients will expect short writes at EOF, so we have to limit * offset+size to the image length. */ - blk_len =3D blk_getlength(exp->common.blk); + blk_len =3D blk_co_getlength(exp->common.blk); if (blk_len < 0) { return blk_len; } @@ -992,7 +1018,8 @@ static ssize_t fuse_write(FuseExport *exp, struct fuse= _write_out *out, return -EINVAL; } else if (offset + size > blk_len) { if (exp->growable) { - ret =3D fuse_do_truncate(exp, offset + size, true, PREALLOC_MO= DE_OFF); + ret =3D fuse_co_do_truncate(exp, offset + size, true, + PREALLOC_MODE_OFF); if (ret < 0) { return ret; } @@ -1001,7 +1028,7 @@ static ssize_t fuse_write(FuseExport *exp, struct fus= e_write_out *out, } } =20 - ret =3D blk_pwrite(exp->common.blk, offset, size, buf, 0); + ret =3D blk_co_pwrite(exp->common.blk, offset, size, buf, 0); if (ret < 0) { return ret; } @@ -1016,8 +1043,9 @@ static ssize_t fuse_write(FuseExport *exp, struct fus= e_write_out *out, * Let clients perform various fallocate() operations. * Return 0 on success (no 'out' object), and -errno on error. */ -static ssize_t fuse_fallocate(FuseExport *exp, uint64_t offset, uint64_t l= ength, - uint32_t mode) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_fallocate(FuseExport *exp, + uint64_t offset, uint64_t length, uint32_t mode) { int64_t blk_len; int ret; @@ -1026,7 +1054,7 @@ static ssize_t fuse_fallocate(FuseExport *exp, uint64= _t offset, uint64_t length, return -EACCES; } =20 - blk_len =3D blk_getlength(exp->common.blk); + blk_len =3D blk_co_getlength(exp->common.blk); if (blk_len < 0) { return blk_len; } @@ -1045,14 +1073,14 @@ static ssize_t fuse_fallocate(FuseExport *exp, uint= 64_t offset, uint64_t length, =20 if (offset > blk_len) { /* No preallocation needed here */ - ret =3D fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF); + ret =3D fuse_co_do_truncate(exp, offset, true, PREALLOC_MODE_O= FF); if (ret < 0) { return ret; } } =20 - ret =3D fuse_do_truncate(exp, offset + length, true, - PREALLOC_MODE_FALLOC); + ret =3D fuse_co_do_truncate(exp, offset + length, true, + PREALLOC_MODE_FALLOC); } #ifdef CONFIG_FALLOCATE_PUNCH_HOLE else if (mode & FALLOC_FL_PUNCH_HOLE) { @@ -1063,8 +1091,9 @@ static ssize_t fuse_fallocate(FuseExport *exp, uint64= _t offset, uint64_t length, do { int size =3D MIN(length, BDRV_REQUEST_MAX_BYTES); =20 - ret =3D blk_pwrite_zeroes(exp->common.blk, offset, size, - BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLB= ACK); + ret =3D blk_co_pwrite_zeroes(exp->common.blk, offset, size, + BDRV_REQ_MAY_UNMAP | + BDRV_REQ_NO_FALLBACK); if (ret =3D=3D -ENOTSUP) { /* * fallocate() specifies to return EOPNOTSUPP for unsuppor= ted @@ -1082,8 +1111,8 @@ static ssize_t fuse_fallocate(FuseExport *exp, uint64= _t offset, uint64_t length, else if (mode & FALLOC_FL_ZERO_RANGE) { if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + length > blk_len) { /* No need for zeroes, we are going to write them ourselves */ - ret =3D fuse_do_truncate(exp, offset + length, false, - PREALLOC_MODE_OFF); + ret =3D fuse_co_do_truncate(exp, offset + length, false, + PREALLOC_MODE_OFF); if (ret < 0) { return ret; } @@ -1092,8 +1121,8 @@ static ssize_t fuse_fallocate(FuseExport *exp, uint64= _t offset, uint64_t length, do { int size =3D MIN(length, BDRV_REQUEST_MAX_BYTES); =20 - ret =3D blk_pwrite_zeroes(exp->common.blk, - offset, size, 0); + ret =3D blk_co_pwrite_zeroes(exp->common.blk, + offset, size, 0); offset +=3D size; length -=3D size; } while (ret =3D=3D 0 && length > 0); @@ -1110,9 +1139,9 @@ static ssize_t fuse_fallocate(FuseExport *exp, uint64= _t offset, uint64_t length, * Let clients fsync the exported image. * Return 0 on success (no 'out' object), and -errno on error. */ -static ssize_t fuse_fsync(FuseExport *exp) +static ssize_t coroutine_fn GRAPH_RDLOCK fuse_co_fsync(FuseExport *exp) { - return blk_flush(exp->common.blk); + return blk_co_flush(exp->common.blk); } =20 /** @@ -1120,9 +1149,9 @@ static ssize_t fuse_fsync(FuseExport *exp) * notes this to be a way to return last-minute errors.) * Return 0 on success (no 'out' object), and -errno on error. */ -static ssize_t fuse_flush(FuseExport *exp) +static ssize_t coroutine_fn GRAPH_RDLOCK fuse_co_flush(FuseExport *exp) { - return blk_flush(exp->common.blk); + return blk_co_flush(exp->common.blk); } =20 #ifdef CONFIG_FUSE_LSEEK @@ -1130,8 +1159,9 @@ static ssize_t fuse_flush(FuseExport *exp) * Let clients inquire allocation status. * Return the number of bytes written to *out on success, and -errno on er= ror. */ -static ssize_t fuse_lseek(FuseExport *exp, struct fuse_lseek_out *out, - uint64_t offset, uint32_t whence) +static ssize_t coroutine_fn GRAPH_RDLOCK +fuse_co_lseek(FuseExport *exp, struct fuse_lseek_out *out, + uint64_t offset, uint32_t whence) { if (whence !=3D SEEK_HOLE && whence !=3D SEEK_DATA) { return -EINVAL; @@ -1141,8 +1171,8 @@ static ssize_t fuse_lseek(FuseExport *exp, struct fus= e_lseek_out *out, int64_t pnum; int ret; =20 - ret =3D bdrv_block_status_above(blk_bs(exp->common.blk), NULL, - offset, INT64_MAX, &pnum, NULL, NULL= ); + ret =3D bdrv_co_block_status_above(blk_bs(exp->common.blk), NULL, + offset, INT64_MAX, &pnum, NULL, N= ULL); if (ret < 0) { return ret; } @@ -1159,7 +1189,7 @@ static ssize_t fuse_lseek(FuseExport *exp, struct fus= e_lseek_out *out, * and @blk_len (the client-visible EOF). */ =20 - blk_len =3D blk_getlength(exp->common.blk); + blk_len =3D blk_co_getlength(exp->common.blk); if (blk_len < 0) { return blk_len; } @@ -1303,18 +1333,20 @@ static int fuse_write_buf_response(int fd, /** * Process a FUSE request, incl. writing the response. */ -static void fuse_process_request(FuseExport *exp, - const FuseRequestInHeader *in_hdr, - const void *data_buffer) +static void coroutine_fn +fuse_co_process_request(FuseExport *exp, const FuseRequestInHeader *in_hdr, + const void *data_buffer) { FuseRequestOutHeader out_hdr; /* For read requests: Data to be returned */ void *out_data_buffer =3D NULL; ssize_t ret; =20 + GRAPH_RDLOCK_GUARD(); + switch (in_hdr->common.opcode) { case FUSE_INIT: - ret =3D fuse_init(exp, &out_hdr.init, &in_hdr->init); + ret =3D fuse_co_init(exp, &out_hdr.init, &in_hdr->init); break; =20 case FUSE_DESTROY: @@ -1322,11 +1354,11 @@ static void fuse_process_request(FuseExport *exp, break; =20 case FUSE_STATFS: - ret =3D fuse_statfs(exp, &out_hdr.statfs); + ret =3D fuse_co_statfs(exp, &out_hdr.statfs); break; =20 case FUSE_OPEN: - ret =3D fuse_open(exp, &out_hdr.open); + ret =3D fuse_co_open(exp, &out_hdr.open); break; =20 case FUSE_RELEASE: @@ -1343,19 +1375,19 @@ static void fuse_process_request(FuseExport *exp, return; =20 case FUSE_GETATTR: - ret =3D fuse_getattr(exp, &out_hdr.attr); + ret =3D fuse_co_getattr(exp, &out_hdr.attr); break; =20 case FUSE_SETATTR: { const struct fuse_setattr_in *in =3D &in_hdr->setattr; - ret =3D fuse_setattr(exp, &out_hdr.attr, - in->valid, in->size, in->mode, in->uid, in->gid= ); + ret =3D fuse_co_setattr(exp, &out_hdr.attr, + in->valid, in->size, in->mode, in->uid, in->= gid); break; } =20 case FUSE_READ: { const struct fuse_read_in *in =3D &in_hdr->read; - ret =3D fuse_read(exp, &out_data_buffer, in->offset, in->size); + ret =3D fuse_co_read(exp, &out_data_buffer, in->offset, in->size); break; } =20 @@ -1373,36 +1405,36 @@ static void fuse_process_request(FuseExport *exp, } =20 /* - * read_from_fuse_fd() has checked that in_hdr->len matches the nu= mber - * of bytes read, which cannot exceed the max_write value we set + * co_read_from_fuse_fd() has checked that in_hdr->len matches the + * number of bytes read, which cannot exceed the max_write value w= e set * (FUSE_MAX_WRITE_BYTES). So we know that FUSE_MAX_WRITE_BYTES >= =3D * in_hdr->len >=3D in->size + X, so this assertion must hold. */ assert(in->size <=3D FUSE_MAX_WRITE_BYTES); =20 - ret =3D fuse_write(exp, &out_hdr.write, - in->offset, in->size, data_buffer); + ret =3D fuse_co_write(exp, &out_hdr.write, + in->offset, in->size, data_buffer); break; } =20 case FUSE_FALLOCATE: { const struct fuse_fallocate_in *in =3D &in_hdr->fallocate; - ret =3D fuse_fallocate(exp, in->offset, in->length, in->mode); + ret =3D fuse_co_fallocate(exp, in->offset, in->length, in->mode); break; } =20 case FUSE_FSYNC: - ret =3D fuse_fsync(exp); + ret =3D fuse_co_fsync(exp); break; =20 case FUSE_FLUSH: - ret =3D fuse_flush(exp); + ret =3D fuse_co_flush(exp); break; =20 #ifdef CONFIG_FUSE_LSEEK case FUSE_LSEEK: { const struct fuse_lseek_in *in =3D &in_hdr->lseek; - ret =3D fuse_lseek(exp, &out_hdr.lseek, in->offset, in->whence); + ret =3D fuse_co_lseek(exp, &out_hdr.lseek, in->offset, in->whence); break; } #endif --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160198; cv=none; d=zohomail.com; s=zohoarc; b=Fqlv/yrj2t57lb9vTF1cct3XamY3pGxP7S4zo4tOBepj5wrhphCLxm+saomzXWMia2YdGg5LcFPmFXa9GfxHkRCfan24AkCZE/S2EpkG0ZNaPjvV6ys/wNqnJRtNJXFXDsvNdOBYxITo+BgTPnMec3GzkMr7ytvDfhRa9IuEQBc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160198; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=wUgR0eHSb5Bru32ma7pgv8Axs3ei32fA63n6ycrzGsE=; b=cB9BoGoCVvJJpNu3kfhi8SZrB4qNVGoElZF79IeTsfWVXq371BAJ/kJqNHiEXHmAe7ql0D1cQ1bzHosFyyh1a11PMc728XZYJhSlZ8GE+n3YltxGTcLtKXyYbSC8Ft6LbMqV0WCEQrLiVlQGEvU9WMzHqE82wwrMQVed3KfAIlQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160197995813.2804921237795; Tue, 10 Mar 2026 09:29:57 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvX-0003ja-7Z; Tue, 10 Mar 2026 12:27:23 -0400 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 1vzzvK-0003RF-QB for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:12 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvI-0000wx-KV for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:10 -0400 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-688-GQMmBT7EMxemIdtkivbjAw-1; Tue, 10 Mar 2026 12:27:03 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 04D791955F44; Tue, 10 Mar 2026 16:27:03 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CE7E019560A6; Tue, 10 Mar 2026 16:27:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160026; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wUgR0eHSb5Bru32ma7pgv8Axs3ei32fA63n6ycrzGsE=; b=fYR/R26pBL/jM3d7IZUXnihZKUoEuoLmYOFMA9ZQJHzAbQTtsNwQ6ANpyL60PgJMgYL7gX ZkcqXDUQUdrOg9+74CK5y8JQnfR4kgJ5lIKv+qqfN0EOhXjVhN+quJe+FUC2ARh3mYzcB3 P5HRypH8Mp4K+cD/7taKxXDqraFG0Fo= X-MC-Unique: GQMmBT7EMxemIdtkivbjAw-1 X-Mimecast-MFC-AGG-ID: GQMmBT7EMxemIdtkivbjAw_1773160023 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 20/28] block/export: Add multi-threading interface Date: Tue, 10 Mar 2026 17:26:14 +0100 Message-ID: <20260310162622.333137-21-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160215040158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Make BlockExportType.iothread an alternate between a single-thread variant 'str' and a multi-threading variant '[str]'. In contrast to the single-thread setting, the multi-threading setting will not change the BDS's context (and so is incompatible with the fixed-iothread setting), but instead just pass a list to the export driver, with which it can do whatever it wants. Currently no export driver supports multi-threading, so they all return an error when receiving such a list. Suggested-by: Kevin Wolf Acked-by: Markus Armbruster Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-21-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qapi/block-export.json | 36 ++++++++++++++++++--- include/block/export.h | 12 +++++-- block/export/export.c | 48 +++++++++++++++++++++++++--- block/export/fuse.c | 7 ++++ block/export/vduse-blk.c | 7 ++++ block/export/vhost-user-blk-server.c | 8 +++++ nbd/server.c | 6 ++++ 7 files changed, 113 insertions(+), 11 deletions(-) diff --git a/qapi/block-export.json b/qapi/block-export.json index 076954ef1ac..160cd2e3ca0 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -363,14 +363,16 @@ # to the export before completion is signalled. (since: 5.2; # default: false) # -# @iothread: The name of the iothread object where the export will -# run. The default is to use the thread currently associated with -# the block node. (since: 5.2) +# @iothread: The name(s) of one or more iothread object(s) where the +# export will run. The default is to use the thread currently +# associated with the block node. (since: 5.2; multi-threading +# since 10.1) # # @fixed-iothread: True prevents the block node from being moved to # another thread while the export is active. If true and # @iothread is given, export creation fails if the block node -# cannot be moved to the iothread. The default is false. +# cannot be moved to the iothread. Must not be true when giving +# multiple iothreads for @iothread. The default is false. # (since: 5.2) # # @allow-inactive: If true, the export allows the exported node to be @@ -387,7 +389,7 @@ 'base': { 'type': 'BlockExportType', 'id': 'str', '*fixed-iothread': 'bool', - '*iothread': 'str', + '*iothread': 'BlockExportIothreads', 'node-name': 'str', '*writable': 'bool', '*writethrough': 'bool', @@ -403,6 +405,30 @@ 'if': 'CONFIG_VDUSE_BLK_EXPORT' } } } =20 +## +# @BlockExportIothreads: +# +# Specify a single or multiple I/O threads in which to run a block +# export's I/O. +# +# @single: Run the export's I/O in the given single I/O thread. +# +# @multi: Use multi-threading across the given set of I/O threads, +# which must not be empty. Note: Passing a single I/O thread via +# this variant is still treated as multi-threading, which is +# different from using the @single variant. In particular, even +# if there only is a single I/O thread in the set, export types +# that do not support multi-threading will generally reject this +# variant, and BlockExportOptions.fixed-iothread is always +# incompatible with it. +# +# Since: 10.1 +## +{ 'alternate': 'BlockExportIothreads', + 'data': { + 'single': 'str', + 'multi': ['str'] } } + ## # @block-export-add: # diff --git a/include/block/export.h b/include/block/export.h index 4bd9531d4d9..ca45da928c7 100644 --- a/include/block/export.h +++ b/include/block/export.h @@ -32,8 +32,16 @@ typedef struct BlockExportDriver { /* True if the export type supports running on an inactive node */ bool supports_inactive; =20 - /* Creates and starts a new block export */ - int (*create)(BlockExport *, BlockExportOptions *, Error **); + /* + * Creates and starts a new block export. + * + * If the user passed a set of I/O threads for multi-threading, @multi= thread + * is a list of the @multithread_count corresponding contexts (freed b= y the + * caller). Note that @exp->ctx has no relation to that list. + */ + int (*create)(BlockExport *exp, BlockExportOptions *opts, + AioContext *const *multithread, size_t multithread_count, + Error **errp); =20 /* * Frees a removed block export. This function is only called after all diff --git a/block/export/export.c b/block/export/export.c index f3bbf11070d..b733f269f3e 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -76,16 +76,26 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Er= ror **errp) { bool fixed_iothread =3D export->has_fixed_iothread && export->fixed_io= thread; bool allow_inactive =3D export->has_allow_inactive && export->allow_in= active; + bool multithread =3D export->iothread && + export->iothread->type =3D=3D QTYPE_QLIST; const BlockExportDriver *drv; BlockExport *exp =3D NULL; BlockDriverState *bs; BlockBackend *blk =3D NULL; AioContext *ctx; + AioContext **multithread_ctxs =3D NULL; + size_t multithread_count =3D 0; uint64_t perm; int ret; =20 GLOBAL_STATE_CODE(); =20 + if (fixed_iothread && multithread) { + error_setg(errp, + "Cannot use fixed-iothread for a multi-threaded export"= ); + return NULL; + } + if (!id_wellformed(export->id)) { error_setg(errp, "Invalid block export id"); return NULL; @@ -116,14 +126,16 @@ BlockExport *blk_exp_add(BlockExportOptions *export, = Error **errp) =20 ctx =3D bdrv_get_aio_context(bs); =20 - if (export->iothread) { + /* Move the BDS to the target I/O thread, if it is a single one */ + if (export->iothread && !multithread) { + const char *iothread_id =3D export->iothread->u.single; IOThread *iothread; AioContext *new_ctx; Error **set_context_errp; =20 - iothread =3D iothread_by_id(export->iothread); + iothread =3D iothread_by_id(iothread_id); if (!iothread) { - error_setg(errp, "iothread \"%s\" not found", export->iothread= ); + error_setg(errp, "iothread \"%s\" not found", iothread_id); goto fail; } =20 @@ -137,6 +149,32 @@ BlockExport *blk_exp_add(BlockExportOptions *export, E= rror **errp) } else if (fixed_iothread) { goto fail; } + } else if (multithread) { + strList *iothread_list =3D export->iothread->u.multi; + size_t i; + + multithread_count =3D 0; + for (strList *e =3D iothread_list; e; e =3D e->next) { + multithread_count++; + } + + if (multithread_count =3D=3D 0) { + error_setg(errp, "The set of I/O threads must not be empty"); + return NULL; + } + + multithread_ctxs =3D g_new(AioContext *, multithread_count); + i =3D 0; + for (strList *e =3D iothread_list; e; e =3D e->next) { + IOThread *iothread =3D iothread_by_id(e->value); + + if (!iothread) { + error_setg(errp, "iothread \"%s\" not found", e->value); + goto fail; + } + multithread_ctxs[i++] =3D iothread_get_aio_context(iothread); + } + assert(i =3D=3D multithread_count); } =20 bdrv_graph_rdlock_main_loop(); @@ -195,7 +233,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Er= ror **errp) .blk =3D blk, }; =20 - ret =3D drv->create(exp, export, errp); + ret =3D drv->create(exp, export, multithread_ctxs, multithread_count, = errp); if (ret < 0) { goto fail; } @@ -203,6 +241,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Er= ror **errp) assert(exp->blk !=3D NULL); =20 QLIST_INSERT_HEAD(&block_exports, exp, next); + g_free(multithread_ctxs); return exp; =20 fail: @@ -214,6 +253,7 @@ fail: g_free(exp->id); g_free(exp); } + g_free(multithread_ctxs); return NULL; } =20 diff --git a/block/export/fuse.c b/block/export/fuse.c index 7c072409d83..1c794118393 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -259,6 +259,8 @@ static const BlockDevOps fuse_export_blk_dev_ops =3D { =20 static int fuse_export_create(BlockExport *blk_exp, BlockExportOptions *blk_exp_args, + AioContext *const *multithread, + size_t mt_count, Error **errp) { ERRP_GUARD(); /* ensure clean-up even with error_fatal */ @@ -268,6 +270,11 @@ static int fuse_export_create(BlockExport *blk_exp, =20 assert(blk_exp_args->type =3D=3D BLOCK_EXPORT_TYPE_FUSE); =20 + if (multithread) { + error_setg(errp, "FUSE export does not support multi-threading"); + return -EINVAL; + } + /* For growable and writable exports, take the RESIZE permission */ if (args->growable || blk_exp_args->writable) { uint64_t blk_perm, blk_shared_perm; diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c index 8af13b7f0bf..10dc673c566 100644 --- a/block/export/vduse-blk.c +++ b/block/export/vduse-blk.c @@ -267,6 +267,7 @@ static const BlockDevOps vduse_block_ops =3D { }; =20 static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, + AioContext *const *multithread, size_t mt_= count, Error **errp) { VduseBlkExport *vblk_exp =3D container_of(exp, VduseBlkExport, export); @@ -302,6 +303,12 @@ static int vduse_blk_exp_create(BlockExport *exp, Bloc= kExportOptions *opts, return -EINVAL; } } + + if (multithread) { + error_setg(errp, "vduse-blk export does not support multi-threadin= g"); + return -EINVAL; + } + vblk_exp->num_queues =3D num_queues; vblk_exp->handler.blk =3D exp->blk; vblk_exp->handler.serial =3D g_strdup(vblk_opts->serial ?: ""); diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user= -blk-server.c index a4d54e824f2..e89422bb85a 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -316,6 +316,7 @@ static const BlockDevOps vu_blk_dev_ops =3D { }; =20 static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, + AioContext *const *multithread, size_t mt_cou= nt, Error **errp) { VuBlkExport *vexp =3D container_of(exp, VuBlkExport, export); @@ -341,6 +342,13 @@ static int vu_blk_exp_create(BlockExport *exp, BlockEx= portOptions *opts, error_setg(errp, "num-queues must be greater than 0"); return -EINVAL; } + + if (multithread) { + error_setg(errp, + "vhost-user-blk export does not support multi-threading= "); + return -EINVAL; + } + vexp->handler.blk =3D exp->blk; vexp->handler.serial =3D g_strdup("vhost_user_blk"); vexp->handler.logical_block_size =3D logical_block_size; diff --git a/nbd/server.c b/nbd/server.c index acec0487a8b..620097c58ca 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1795,6 +1795,7 @@ static const BlockDevOps nbd_block_ops =3D { }; =20 static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp= _args, + AioContext *const *multithread, size_t mt_cou= nt, Error **errp) { NBDExport *exp =3D container_of(blk_exp, NBDExport, common); @@ -1831,6 +1832,11 @@ static int nbd_export_create(BlockExport *blk_exp, B= lockExportOptions *exp_args, return -EEXIST; } =20 + if (multithread) { + error_setg(errp, "NBD export does not support multi-threading"); + return -EINVAL; + } + size =3D blk_getlength(blk); if (size < 0) { error_setg_errno(errp, -size, --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160394; cv=none; d=zohomail.com; s=zohoarc; b=PlsDAmVgHP85vCT957cMWOQIrxbgBrnV1vLDr0XHu8Pe3VFjdT4Mn36y4Y3XRA7qhFPqXIFgbiVJd6qabjnIWbG/xYviQWvxX6xZ+kuoSAqTov/+8l99y/N/GawLVjli/X/FogsBfl9jNbjjFmedd/1OCWG82jotf0UebzkwMvs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160394; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=KMADtodgxED6gXdhsqP/rjK9lfC+lRgElo+09eD/r3w=; b=SNaLdktit5bwlMGeMHuXOXQukTLDMChfylQ7AY/sUyADocaXQH+K4g4NyGmoNla+Js3iqK4Y3QDKTASYrQjsZgf28bDWr1LSYIg6hvFESVEEXCOiXrPVXIYErVeE8R8e0ThoyqZ8kXbI4qQPfwTGkIlYXzG0rGhS8zZw9D8nKXE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160394364648.3899789862612; Tue, 10 Mar 2026 09:33:14 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvW-0003fQ-8L; Tue, 10 Mar 2026 12:27:22 -0400 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 1vzzvK-0003R2-Pz for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:12 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvI-0000wt-82 for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:09 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-170-Ga04mpZ5NMu_sP4JrOARGA-1; Tue, 10 Mar 2026 12:27:05 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 95FA81956057; Tue, 10 Mar 2026 16:27:04 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6B9E619560A6; Tue, 10 Mar 2026 16:27:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160026; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KMADtodgxED6gXdhsqP/rjK9lfC+lRgElo+09eD/r3w=; b=RvMyeqWp3mDQ0cIOQkAPCyrA3aeOJ2p3rApwyWUjm3B9UojTtKXRx1Zx4sh6QBk3xIOAVZ i5q7qwDbzqzi+XDvU9m2Yx8ep4ZhTJKpu5QKCrHZjf8JrvTeKLh7seujpZTptvNPndY918 SnkvG6TtoL0z61JeNLfmt8t/3nrG87A= X-MC-Unique: Ga04mpZ5NMu_sP4JrOARGA-1 X-Mimecast-MFC-AGG-ID: Ga04mpZ5NMu_sP4JrOARGA_1773160024 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 21/28] iotests/307: Test multi-thread export interface Date: Tue, 10 Mar 2026 17:26:15 +0100 Message-ID: <20260310162622.333137-22-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160394945158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Test the QAPI interface for multi-threaded exports. None of our exports currently support multi-threading, so it's always an error in the end, but we can still test the specific errors. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-22-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/307 | 47 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/307.out | 18 +++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/tests/qemu-iotests/307 b/tests/qemu-iotests/307 index b429b5aa50a..f6ee3ebec08 100755 --- a/tests/qemu-iotests/307 +++ b/tests/qemu-iotests/307 @@ -142,5 +142,52 @@ with iotests.FilePath('image') as img, \ vm.qmp_log('query-block-exports') iotests.qemu_nbd_list_log('-k', socket) =20 + iotests.log('\n=3D=3D=3D Using multi-thread with NBD =3D=3D=3D') + + # Actual multi-threading; (currently) not supported by NBD + vm.qmp_log('block-export-add', + id=3D'export0', + type=3D'nbd', + node_name=3D'fmt', + iothread=3D['iothread0', 'iothread1']) + + # Should be treated the same way as actual multi-threading, even if th= ere's + # only a single thread + vm.qmp_log('block-export-add', + id=3D'export0', + type=3D'nbd', + node_name=3D'fmt', + iothread=3D['iothread0']) + + iotests.log('\n=3D=3D=3D Empty thread list') + + # Simply not allowed + vm.qmp_log('block-export-add', + id=3D'export0', + type=3D'nbd', + node_name=3D'fmt', + iothread=3D[]) + + iotests.log('\n=3D=3D=3D Non-existent thread name in list') + + # Expect an error, even if NBD does not support multi-threading, becau= se the + # list is parsed before being passed to NBD + vm.qmp_log('block-export-add', + id=3D'export0', + type=3D'nbd', + node_name=3D'fmt', + iothread=3D['iothread0', 'nothread', 'iothread1']) + + iotests.log('\n=3D=3D=3D Multi-thread with fixed-iothread') + + # With multi-threading, there is no single context to give the BDS, so= it is + # just left where it is. fixed-iothread does not make sense then. + vm.qmp_log('block-export-add', + id=3D'export0', + type=3D'nbd', + node_name=3D'fmt', + iothread=3D['iothread0', 'iothread1'], + fixed_iothread=3DTrue) + iotests.log('\n=3D=3D=3D Shut down QEMU =3D=3D=3D') vm.shutdown() diff --git a/tests/qemu-iotests/307.out b/tests/qemu-iotests/307.out index f645f3315f8..a9b37d3ac11 100644 --- a/tests/qemu-iotests/307.out +++ b/tests/qemu-iotests/307.out @@ -134,4 +134,22 @@ read failed: Input/output error exports available: 0 =20 =20 +=3D=3D=3D Using multi-thread with NBD =3D=3D=3D +{"execute": "block-export-add", "arguments": {"id": "export0", "iothread":= ["iothread0", "iothread1"], "node-name": "fmt", "type": "nbd"}} +{"error": {"class": "GenericError", "desc": "NBD export does not support m= ulti-threading"}} +{"execute": "block-export-add", "arguments": {"id": "export0", "iothread":= ["iothread0"], "node-name": "fmt", "type": "nbd"}} +{"error": {"class": "GenericError", "desc": "NBD export does not support m= ulti-threading"}} + +=3D=3D=3D Empty thread list +{"execute": "block-export-add", "arguments": {"id": "export0", "iothread":= [], "node-name": "fmt", "type": "nbd"}} +{"error": {"class": "GenericError", "desc": "The set of I/O threads must n= ot be empty"}} + +=3D=3D=3D Non-existent thread name in list +{"execute": "block-export-add", "arguments": {"id": "export0", "iothread":= ["iothread0", "nothread", "iothread1"], "node-name": "fmt", "type": "nbd"}} +{"error": {"class": "GenericError", "desc": "iothread \"nothread\" not fou= nd"}} + +=3D=3D=3D Multi-thread with fixed-iothread +{"execute": "block-export-add", "arguments": {"fixed-iothread": true, "id"= : "export0", "iothread": ["iothread0", "iothread1"], "node-name": "fmt", "t= ype": "nbd"}} +{"error": {"class": "GenericError", "desc": "Cannot use fixed-iothread for= a multi-threaded export"}} + =3D=3D=3D Shut down QEMU =3D=3D=3D --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160185; cv=none; d=zohomail.com; s=zohoarc; b=MwKOJH2WEIKEZuAKeAFhbwKBJK2JzD/ynGKnyrKFhmxVgRo8khK6G+wkMErWNIlGDXjllX95EjZiIOfnD9c2yv1RjFZObO4doahUYkjIyIC3DSfVbDPzasX3+3m3fSXNnmkY7b6ekOAlb0Hh9/2YPCxmcD+pAWzfH1uxHUNu5y4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160185; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=580OLnu98MzO5i32Lhtd2MCPaiQqex91cw2Qk/SXWUA=; b=G4R0zDC4zKbZx7gTeb6JQH8GpKY1cMHTUhFLcEAe6SP0QCdLXvAXwubm22UF010RMptBMplRZm5NwowQVIJtBgwr0aCOyc6zK+XnGrTWj2O/w8cst7R6LA/ox9cScoJv9/ly6PuJw8n3CRpveRprn6sL5IT5vykMcIr1AVFTWNE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160185008907.4502477067151; Tue, 10 Mar 2026 09:29:45 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvX-0003jD-1m; Tue, 10 Mar 2026 12:27:23 -0400 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 1vzzvM-0003SA-Td for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:14 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvL-0000y0-8M for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:12 -0400 Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-625--gb67MDpM1SwFimbAfFx1Q-1; Tue, 10 Mar 2026 12:27:07 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 938491800625; Tue, 10 Mar 2026 16:27:06 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0B5C419560A6; Tue, 10 Mar 2026 16:27:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160030; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=580OLnu98MzO5i32Lhtd2MCPaiQqex91cw2Qk/SXWUA=; b=hUbgDa15H6CTJg8eFmk5v+V2+lnMj6GuWlRALB9FfHXQmyXIGOmMce3OdphiTePD+nXqkv oI2IGhvnwmhbMHHiSJtj3Shhn7GKGcbYGO6uPeAfnvnEo84469gh8DlT19Vjd8kgvTGtfj FgLnVkBgF09N/nVoacQky/DZLJSIe3c= X-MC-Unique: -gb67MDpM1SwFimbAfFx1Q-1 X-Mimecast-MFC-AGG-ID: -gb67MDpM1SwFimbAfFx1Q_1773160026 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 22/28] fuse: Make shared export state atomic Date: Tue, 10 Mar 2026 17:26:16 +0100 Message-ID: <20260310162622.333137-23-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160186514154100 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek The next commit is going to allow multi-threaded access to a FUSE export. In order to allow safe concurrent SETATTR operations that can modify the shared st_mode, st_uid, and st_gid, make any access to those fields atomic operations. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-23-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 1c794118393..fe1b6ad5ffc 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -156,6 +156,7 @@ typedef struct FuseExport { /* Whether allow_other was used as a mount option or not */ bool allow_other; =20 + /* All atomic */ mode_t st_mode; uid_t st_uid; gid_t st_gid; @@ -266,6 +267,7 @@ static int fuse_export_create(BlockExport *blk_exp, ERRP_GUARD(); /* ensure clean-up even with error_fatal */ FuseExport *exp =3D container_of(blk_exp, FuseExport, common); BlockExportOptionsFuse *args =3D &blk_exp_args->u.fuse; + uint32_t st_mode; int ret; =20 assert(blk_exp_args->type =3D=3D BLOCK_EXPORT_TYPE_FUSE); @@ -334,12 +336,13 @@ static int fuse_export_create(BlockExport *blk_exp, args->allow_other =3D FUSE_EXPORT_ALLOW_OTHER_AUTO; } =20 - exp->st_mode =3D S_IFREG | S_IRUSR; + st_mode =3D S_IFREG | S_IRUSR; if (exp->writable) { - exp->st_mode |=3D S_IWUSR; + st_mode |=3D S_IWUSR; } - exp->st_uid =3D getuid(); - exp->st_gid =3D getgid(); + qatomic_set(&exp->st_mode, st_mode); + qatomic_set(&exp->st_uid, getuid()); + qatomic_set(&exp->st_gid, getgid()); =20 if (args->allow_other =3D=3D FUSE_EXPORT_ALLOW_OTHER_AUTO) { /* Try allow_other =3D=3D true first, ignore errors */ @@ -817,10 +820,10 @@ fuse_co_getattr(FuseExport *exp, struct fuse_attr_out= *out) .attr_valid =3D 1, .attr =3D { .ino =3D 1, - .mode =3D exp->st_mode, + .mode =3D qatomic_read(&exp->st_mode), .nlink =3D 1, - .uid =3D exp->st_uid, - .gid =3D exp->st_gid, + .uid =3D qatomic_read(&exp->st_uid), + .gid =3D qatomic_read(&exp->st_gid), .size =3D length, .blksize =3D blk_bs(exp->common.blk)->bl.request_alignment, .blocks =3D allocated_blocks, @@ -903,15 +906,15 @@ fuse_co_setattr(FuseExport *exp, struct fuse_attr_out= *out, uint32_t to_set, =20 if (to_set & FATTR_MODE) { /* Ignore FUSE-supplied file type, only change the mode */ - exp->st_mode =3D (mode & 07777) | S_IFREG; + qatomic_set(&exp->st_mode, (mode & 07777) | S_IFREG); } =20 if (to_set & FATTR_UID) { - exp->st_uid =3D uid; + qatomic_set(&exp->st_uid, uid); } =20 if (to_set & FATTR_GID) { - exp->st_gid =3D gid; + qatomic_set(&exp->st_gid, gid); } =20 return fuse_co_getattr(exp, out); --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160172; cv=none; d=zohomail.com; s=zohoarc; b=PhOJRyAKi/YyOwqiKX0OgrrJW6uegDZnmicp+mEcMNV+vEp3FsXtFFSRnIWiWaEpTAyy4lnKVif7AjVgcp4mpmjslqmrCWV9K4dX7S4Fq48+5mYd6e3bYoRdJ8rcY1Oqxk+pXXwmwExLyZ2kjTrWZaDovw8+mmzJy/wHtzfCI2c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160172; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=IzgPE2qoJKJk1GXEHyIV3nzDp+WR6Nwuypa/cvTvsiw=; b=aT140aDJVqc2yditqD1cyVdA2+sYqhmfF/jpazQ3qVP54t47h4HPQo9Uj2XoiZdHMcECTN665lCtsDTTW6oruAoVhWzN0atjdHZKprAHlfw+9mB6YenmY5DRXLjmVSOT8Ud1wJYzPf5iGF4PG7k5nOQpJ7bNDUy/D+IYVmsRpxg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177316017230876.66562341212887; Tue, 10 Mar 2026 09:29:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvZ-0003tC-6H; Tue, 10 Mar 2026 12:27:25 -0400 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 1vzzvP-0003SV-JV for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:15 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvM-0000yI-FE for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:14 -0400 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-96-Mjw5b1b6PkCqcTGgZzXgaA-1; Tue, 10 Mar 2026 12:27:09 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2D70C1955F29; Tue, 10 Mar 2026 16:27:08 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0941019560A6; Tue, 10 Mar 2026 16:27:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160031; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IzgPE2qoJKJk1GXEHyIV3nzDp+WR6Nwuypa/cvTvsiw=; b=EqP7SoGMEKUTYuH0A7A+y04t2ymBK/GZo7jV6+QqQ7uusBhgFZkCMJJY8gnwffLYH5fPij Fex6hP1efLzv43x4xSfSkRsVZ4fzkm3Gyh+gABIUILzcqUrqnEdR3SDxjGfgTNXnbGQKil LjOvrqJVOIHy+nzUdMvzxS3kdrZenYo= X-MC-Unique: Mjw5b1b6PkCqcTGgZzXgaA-1 X-Mimecast-MFC-AGG-ID: Mjw5b1b6PkCqcTGgZzXgaA_1773160028 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 23/28] fuse: Implement multi-threading Date: Tue, 10 Mar 2026 17:26:17 +0100 Message-ID: <20260310162622.333137-24-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160184951158500 From: Hanna Czenczek FUSE allows creating multiple request queues by "cloning" /dev/fuse FDs (via open("/dev/fuse") + ioctl(FUSE_DEV_IOC_CLONE)). We can use this to implement multi-threading. For configuration, we don't need any more information beyond the simple array provided by the core block export interface: The FUSE kernel driver feeds these FDs in a round-robin fashion, so all of them are equivalent and we want to have exactly one per thread. These are the benchmark results when using four threads (compared to a single thread); note that fio still only uses a single job, but performance can still be improved because of said round-robin usage for the queues. (Not in the sync case, though, in which case I guess it just adds overhead.) file: read: seq aio: 261.7k =C2=B11.7k (+168%) rand aio: 129.2k =C2=B114.3k (+35%) seq sync: 36.6k =C2=B10.6k (+6%) rand sync: 10.1k =C2=B10.1k (+2%) write: seq aio: 235.7k =C2=B12.8k (+243%) rand aio: 232.0k =C2=B16.7k (+237%) seq sync: 31.7k =C2=B10.6k (+4%) rand sync: 31.8k =C2=B10.5k (+4%) null: read: seq aio: 253.8k =C2=B112.3k (+45%) rand aio: 248.2k =C2=B112.0k (+45%) seq sync: 91.6k =C2=B12.4k (+12%) rand sync: 91.3k =C2=B12.1k (+17%) write: seq aio: 208.2k =C2=B19.8k (+6%) rand aio: 207.0k =C2=B17.4k (+8%) seq sync: 91.2k =C2=B11.9k (+9%) rand sync: 90.4k =C2=B12.5k (+14%) So moderate improvements in most cases, but quite improved AIO performance with an actual underlying file. Here's results for numjobs=3D4: "Before", i.e. without multithreading in QSD/FUSE (results compared to numjobs=3D1): file: read: seq aio: 85.5k =C2=B10.4k (-13%) rand aio: 92.5k =C2=B10.5k (-3%) seq sync: 54.5k =C2=B19.1k (+58%) rand sync: 38.0k =C2=B10.2k (+283%) write: seq aio: 67.3k =C2=B10.3k (-2%) rand aio: 67.6k =C2=B10.3k (-2%) seq sync: 69.3k =C2=B10.5k (+126%) rand sync: 69.3k =C2=B10.3k (+126%) null: read: seq aio: 170.6k =C2=B10.8k (-2%) rand aio: 170.9k =C2=B10.9k (=C2=B10%) seq sync: 187.6k =C2=B11.3k (+129%) rand sync: 188.9k =C2=B10.9k (+142%) write: seq aio: 191.5k =C2=B11.2k (-2%) rand aio: 193.8k =C2=B11.4k (-1%) seq sync: 206.1k =C2=B11.3k (+147%) rand sync: 206.1k =C2=B11.2k (+159%) As probably expected, little difference in the AIO case, but great improvements in the sync cases because it kind of gives it an artificial iodepth of 4. "After", i.e. with four threads in QSD/FUSE (now results compared to the above): file: read: seq aio: 198.7k =C2=B12.7k (+132%) rand aio: 317.3k =C2=B10.6k (+243%) seq sync: 55.9k =C2=B18.9k (+3%) rand sync: 39.1k =C2=B10.0k (+3%) write: seq aio: 229.0k =C2=B10.8k (+240%) rand aio: 227.0k =C2=B11.3k (+235%) seq sync: 102.5k =C2=B10.2k (+48%) rand sync: 101.7k =C2=B10.2k (+47%) null: read: seq aio: 584.0k =C2=B11.5k (+242%) rand aio: 581.9k =C2=B11.9k (+240%) seq sync: 270.6k =C2=B10.9k (+44%) rand sync: 270.4k =C2=B10.7k (+43%) write: seq aio: 598.4k =C2=B12.0k (+212%) rand aio: 605.2k =C2=B12.0k (+212%) seq sync: 274.0k =C2=B10.8k (+33%) rand sync: 275.0k =C2=B10.7k (+33%) So this helps mainly for the AIO cases, but also in the null sync cases, because null is always CPU-bound, so more threads help. One unsolved mystery: When using a multithreaded export, running fio with 1 job (benchmark at the top of this commit) yields better seqread performance than doing so with 4 jobs. Actually, with 4 jobs, it's significantly than randread, which is quite strange. Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-24-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 193 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 153 insertions(+), 40 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index fe1b6ad5ffc..a2a478d2934 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -31,11 +31,13 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "system/block-backend.h" +#include "system/iothread.h" =20 #include #include =20 #include "standard-headers/linux/fuse.h" +#include =20 #if defined(CONFIG_FALLOCATE_ZERO_RANGE) #include @@ -118,12 +120,17 @@ QEMU_BUILD_BUG_ON(sizeof(((FuseRequestInHeaderBuf *)0= )->head) + sizeof(((FuseRequestInHeaderBuf *)0)->tail) !=3D sizeof(FuseRequestInHeader)); =20 -typedef struct FuseExport { - BlockExport common; +typedef struct FuseExport FuseExport; =20 - struct fuse_session *fuse_session; - unsigned int in_flight; /* atomic */ - bool mounted, fd_handler_set_up; +/* + * One FUSE "queue", representing one FUSE FD from which requests are fetc= hed + * and processed. Each queue is tied to an AioContext. + */ +typedef struct FuseQueue { + FuseExport *exp; + + AioContext *ctx; + int fuse_fd; =20 /* * Cached buffer to receive the data of WRITE requests. Cached becaus= e: @@ -140,6 +147,14 @@ typedef struct FuseExport { * via blk_blockalign() and thus need to be freed via qemu_vfree(). */ void *req_write_data_cached; +} FuseQueue; + +struct FuseExport { + BlockExport common; + + struct fuse_session *fuse_session; + unsigned int in_flight; /* atomic */ + bool mounted, fd_handler_set_up; =20 /* * Set when there was an unrecoverable error and no requests should be= read @@ -148,7 +163,15 @@ typedef struct FuseExport { */ bool halted; =20 - int fuse_fd; + int num_queues; + FuseQueue *queues; + /* + * True if this export should follow the generic export's AioContext. + * Will be false if the queues' AioContexts have been explicitly set b= y the + * user, i.e. are expected to stay in those contexts. + * (I.e. is always false if there is more than one queue.) + */ + bool follow_aio_context; =20 char *mountpoint; bool writable; @@ -160,7 +183,7 @@ typedef struct FuseExport { mode_t st_mode; uid_t st_uid; gid_t st_gid; -} FuseExport; +}; =20 /* * Verify that the size of FuseRequestInHeaderBuf.head plus the data @@ -179,12 +202,13 @@ static void fuse_export_halt(FuseExport *exp); static void init_exports_table(void); =20 static int mount_fuse_export(FuseExport *exp, Error **errp); +static int clone_fuse_fd(int fd, Error **errp); =20 static bool is_regular_file(const char *path, Error **errp); =20 static void read_from_fuse_fd(void *opaque); static void coroutine_fn -fuse_co_process_request(FuseExport *exp, const FuseRequestInHeader *in_hdr, +fuse_co_process_request(FuseQueue *q, const FuseRequestInHeader *in_hdr, const void *data_buffer); static int fuse_write_err(int fd, const struct fuse_in_header *in_hdr, int= err); =20 @@ -216,8 +240,11 @@ static void fuse_attach_handlers(FuseExport *exp) return; } =20 - aio_set_fd_handler(exp->common.ctx, exp->fuse_fd, - read_from_fuse_fd, NULL, NULL, NULL, exp); + for (int i =3D 0; i < exp->num_queues; i++) { + aio_set_fd_handler(exp->queues[i].ctx, exp->queues[i].fuse_fd, + read_from_fuse_fd, NULL, NULL, NULL, + &exp->queues[i]); + } exp->fd_handler_set_up =3D true; } =20 @@ -226,8 +253,10 @@ static void fuse_attach_handlers(FuseExport *exp) */ static void fuse_detach_handlers(FuseExport *exp) { - aio_set_fd_handler(exp->common.ctx, exp->fuse_fd, - NULL, NULL, NULL, NULL, NULL); + for (int i =3D 0; i < exp->num_queues; i++) { + aio_set_fd_handler(exp->queues[i].ctx, exp->queues[i].fuse_fd, + NULL, NULL, NULL, NULL, NULL); + } exp->fd_handler_set_up =3D false; } =20 @@ -242,6 +271,11 @@ static void fuse_export_drained_end(void *opaque) =20 /* Refresh AioContext in case it changed */ exp->common.ctx =3D blk_get_aio_context(exp->common.blk); + if (exp->follow_aio_context) { + assert(exp->num_queues =3D=3D 1); + exp->queues[0].ctx =3D exp->common.ctx; + } + fuse_attach_handlers(exp); } =20 @@ -273,8 +307,32 @@ static int fuse_export_create(BlockExport *blk_exp, assert(blk_exp_args->type =3D=3D BLOCK_EXPORT_TYPE_FUSE); =20 if (multithread) { - error_setg(errp, "FUSE export does not support multi-threading"); - return -EINVAL; + /* Guaranteed by common export code */ + assert(mt_count >=3D 1); + + exp->follow_aio_context =3D false; + exp->num_queues =3D mt_count; + exp->queues =3D g_new(FuseQueue, mt_count); + + for (size_t i =3D 0; i < mt_count; i++) { + exp->queues[i] =3D (FuseQueue) { + .exp =3D exp, + .ctx =3D multithread[i], + .fuse_fd =3D -1, + }; + } + } else { + /* Guaranteed by common export code */ + assert(mt_count =3D=3D 0); + + exp->follow_aio_context =3D true; + exp->num_queues =3D 1; + exp->queues =3D g_new(FuseQueue, 1); + exp->queues[0] =3D (FuseQueue) { + .exp =3D exp, + .ctx =3D exp->common.ctx, + .fuse_fd =3D -1, + }; } =20 /* For growable and writable exports, take the RESIZE permission */ @@ -286,7 +344,7 @@ static int fuse_export_create(BlockExport *blk_exp, ret =3D blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE, blk_shared_perm, errp); if (ret < 0) { - return ret; + goto fail; } } =20 @@ -362,13 +420,23 @@ static int fuse_export_create(BlockExport *blk_exp, =20 g_hash_table_insert(exports, g_strdup(exp->mountpoint), NULL); =20 - exp->fuse_fd =3D fuse_session_fd(exp->fuse_session); - ret =3D qemu_fcntl_addfl(exp->fuse_fd, O_NONBLOCK); + assert(exp->num_queues >=3D 1); + exp->queues[0].fuse_fd =3D fuse_session_fd(exp->fuse_session); + ret =3D qemu_fcntl_addfl(exp->queues[0].fuse_fd, O_NONBLOCK); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to make FUSE FD non-blocking"= ); goto fail; } =20 + for (int i =3D 1; i < exp->num_queues; i++) { + int fd =3D clone_fuse_fd(exp->queues[0].fuse_fd, errp); + if (fd < 0) { + ret =3D fd; + goto fail; + } + exp->queues[i].fuse_fd =3D fd; + } + fuse_attach_handlers(exp); return 0; =20 @@ -461,28 +529,28 @@ fail: /** * Allocate a buffer to receive WRITE data, or take the cached one. */ -static void *get_write_data_buffer(FuseExport *exp) +static void *get_write_data_buffer(FuseQueue *q) { - if (exp->req_write_data_cached) { - void *cached =3D exp->req_write_data_cached; - exp->req_write_data_cached =3D NULL; + if (q->req_write_data_cached) { + void *cached =3D q->req_write_data_cached; + q->req_write_data_cached =3D NULL; return cached; } else { - return blk_blockalign(exp->common.blk, FUSE_MAX_WRITE_BYTES); + return blk_blockalign(q->exp->common.blk, FUSE_MAX_WRITE_BYTES); } } =20 /** * Release a WRITE data buffer, possibly reusing it for a subsequent reque= st. */ -static void release_write_data_buffer(FuseExport *exp, void **buffer) +static void release_write_data_buffer(FuseQueue *q, void **buffer) { if (!*buffer) { return; } =20 - if (!exp->req_write_data_cached) { - exp->req_write_data_cached =3D *buffer; + if (!q->req_write_data_cached) { + q->req_write_data_cached =3D *buffer; } else { qemu_vfree(*buffer); } @@ -528,9 +596,42 @@ static ssize_t req_op_hdr_len(const FuseRequestInHeade= r *in_hdr) } } =20 +/** + * Clone the given /dev/fuse file descriptor, yielding a second FD from wh= ich + * requests can be pulled for the associated filesystem. Returns an FD on + * success, and -errno on error. + */ +static int clone_fuse_fd(int fd, Error **errp) +{ + uint32_t src_fd =3D fd; + int new_fd; + int ret; + + /* + * The name "/dev/fuse" is fixed, see libfuse's lib/fuse_loop_mt.c + * (fuse_clone_chan()). + */ + new_fd =3D open("/dev/fuse", O_RDWR | O_CLOEXEC | O_NONBLOCK); + if (new_fd < 0) { + ret =3D -errno; + error_setg_errno(errp, errno, "Failed to open /dev/fuse"); + return ret; + } + + ret =3D ioctl(new_fd, FUSE_DEV_IOC_CLONE, &src_fd); + if (ret < 0) { + ret =3D -errno; + error_setg_errno(errp, errno, "Failed to clone FUSE FD"); + close(new_fd); + return ret; + } + + return new_fd; +} + /** * Try to read a single request from the FUSE FD. - * Takes a FuseExport pointer in `opaque`. + * Takes a FuseQueue pointer in `opaque`. * * Assumes the export's in-flight counter has already been incremented. * @@ -538,8 +639,9 @@ static ssize_t req_op_hdr_len(const FuseRequestInHeader= *in_hdr) */ static void coroutine_fn co_read_from_fuse_fd(void *opaque) { - FuseExport *exp =3D opaque; - int fuse_fd =3D exp->fuse_fd; + FuseQueue *q =3D opaque; + int fuse_fd =3D q->fuse_fd; + FuseExport *exp =3D q->exp; ssize_t ret; FuseRequestInHeaderBuf in_hdr_buf; const FuseRequestInHeader *in_hdr; @@ -551,7 +653,7 @@ static void coroutine_fn co_read_from_fuse_fd(void *opa= que) goto no_request; } =20 - data_buffer =3D get_write_data_buffer(exp); + data_buffer =3D get_write_data_buffer(q); =20 /* Construct the I/O vector to hold the FUSE request */ iov[0] =3D (struct iovec) { &in_hdr_buf.head, sizeof(in_hdr_buf.head) = }; @@ -612,29 +714,29 @@ static void coroutine_fn co_read_from_fuse_fd(void *o= paque) memcpy(in_hdr_buf.tail, data_buffer, len); } =20 - release_write_data_buffer(exp, &data_buffer); + release_write_data_buffer(q, &data_buffer); } =20 - fuse_co_process_request(exp, in_hdr, data_buffer); + fuse_co_process_request(q, in_hdr, data_buffer); =20 no_request: - release_write_data_buffer(exp, &data_buffer); + release_write_data_buffer(q, &data_buffer); fuse_dec_in_flight(exp); } =20 /** * Try to read and process a single request from the FUSE FD. * (To be used as a handler for when the FUSE FD becomes readable.) - * Takes a FuseExport pointer in `opaque`. + * Takes a FuseQueue pointer in `opaque`. */ static void read_from_fuse_fd(void *opaque) { - FuseExport *exp =3D opaque; + FuseQueue *q =3D opaque; Coroutine *co; =20 - co =3D qemu_coroutine_create(co_read_from_fuse_fd, exp); + co =3D qemu_coroutine_create(co_read_from_fuse_fd, q); /* Decremented by co_read_from_fuse_fd() */ - fuse_inc_in_flight(exp); + fuse_inc_in_flight(q->exp); qemu_coroutine_enter(co); } =20 @@ -659,6 +761,17 @@ static void fuse_export_delete(BlockExport *blk_exp) { FuseExport *exp =3D container_of(blk_exp, FuseExport, common); =20 + for (int i =3D 0; i < exp->num_queues; i++) { + FuseQueue *q =3D &exp->queues[i]; + + /* Queue 0's FD belongs to the FUSE session */ + if (i > 0 && q->fuse_fd >=3D 0) { + close(q->fuse_fd); + } + qemu_vfree(q->req_write_data_cached); + } + g_free(exp->queues); + if (exp->fuse_session) { if (exp->mounted) { fuse_session_unmount(exp->fuse_session); @@ -667,7 +780,6 @@ static void fuse_export_delete(BlockExport *blk_exp) fuse_session_destroy(exp->fuse_session); } =20 - qemu_vfree(exp->req_write_data_cached); g_free(exp->mountpoint); } =20 @@ -1344,10 +1456,11 @@ static int fuse_write_buf_response(int fd, * Process a FUSE request, incl. writing the response. */ static void coroutine_fn -fuse_co_process_request(FuseExport *exp, const FuseRequestInHeader *in_hdr, +fuse_co_process_request(FuseQueue *q, const FuseRequestInHeader *in_hdr, const void *data_buffer) { FuseRequestOutHeader out_hdr; + FuseExport *exp =3D q->exp; /* For read requests: Data to be returned */ void *out_data_buffer =3D NULL; ssize_t ret; @@ -1471,10 +1584,10 @@ fuse_co_process_request(FuseExport *exp, const Fuse= RequestInHeader *in_hdr, } =20 if (out_data_buffer) { - fuse_write_buf_response(exp->fuse_fd, &out_hdr.common, out_data_bu= ffer); + fuse_write_buf_response(q->fuse_fd, &out_hdr.common, out_data_buff= er); qemu_vfree(out_data_buffer); } else { - fuse_write_response(exp->fuse_fd, &out_hdr); + fuse_write_response(q->fuse_fd, &out_hdr); } } =20 --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160231; cv=none; d=zohomail.com; s=zohoarc; b=VUZUUgeXtsQBZbxWK/3fyJaHMJG1YCHk4YZN12gJnRc+O8gFG5LbUx2Z113l6D7rnB+PDp3xKGwSJbUp9MxQs237+mToxxMcgaJu5RosSMi4vZfzAahF57MXgv237A+54xoMpUytRdej/dPwc/iUD7twJ2Vy55Z8KpMeThS3RHQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160231; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=tI1C+SlE1JodyxPaQ7ANK5Oh5Fctm+0K3lwwDSO8Nqc=; b=RZeB3VKKv31X0WBOyOxpCPItrQKQQLb7pYzx2LGSuXD5A2qhtSLn3Ext4mB5N9ylw+c8qx0b6sRvvb4HPZjUXNJ0brTdZ0sdYmwVx+XX/lcGb0R6+wfXGDdzfFls8cPM//0vTbAhB6Wbw3zj8A+bk0SoQXJizxKE/dkBaGSCRfI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160231982386.279974499275; Tue, 10 Mar 2026 09:30:31 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvY-0003ph-GN; Tue, 10 Mar 2026 12:27:24 -0400 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 1vzzvO-0003SB-Cu for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:14 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvM-0000yZ-Te for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:14 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-131-mdjk8JVGPsO93P_l7n6o2g-1; Tue, 10 Mar 2026 12:27:10 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D11B718002D0; Tue, 10 Mar 2026 16:27:09 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 97C1C19560A6; Tue, 10 Mar 2026 16:27:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160032; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tI1C+SlE1JodyxPaQ7ANK5Oh5Fctm+0K3lwwDSO8Nqc=; b=Hf4UQExf0SBvJUOhRdYnpCLS1X18lo/BJIvQqucvKCSf36e5l6eIRemazW7Gmg7ka+8fXM ZpYmOghLuDlE2lJyX4DyiurSRLsWJjHtfdkHY+31QOKZ/Xr8gJ/DdV+vx5UXC9+of50PIK wMS6P/lZQ0Pq/s44VWF64SLcpRWElgI= X-MC-Unique: mdjk8JVGPsO93P_l7n6o2g-1 X-Mimecast-MFC-AGG-ID: mdjk8JVGPsO93P_l7n6o2g_1773160029 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 24/28] qapi/block-export: Document FUSE's multi-threading Date: Tue, 10 Mar 2026 17:26:18 +0100 Message-ID: <20260310162622.333137-25-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.129.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160244980158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Document for users that FUSE's multi-threading implementation distributes requests in a round-robin manner, regardless of where they originate from. As noted by Stefan, this will probably change with a FUSE-over-io_uring implementation (which is supposed to have CPU affinity), but documenting that is left for once that is done. Suggested-by: Stefan Hajnoczi Acked-by: Markus Armbruster Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-25-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qapi/block-export.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qapi/block-export.json b/qapi/block-export.json index 160cd2e3ca0..dd724acf1cb 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -164,6 +164,11 @@ # Options for exporting a block graph node on some (file) mountpoint # as a raw image. # +# Multi-threading note: The FUSE export supports multi-threading. +# Currently, requests are distributed across these threads in a +# round-robin fashion, i.e. independently of the CPU core from which a +# request originates. +# # @mountpoint: Path on which to export the block device via FUSE. # This must point to an existing regular file. # --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160171; cv=none; d=zohomail.com; s=zohoarc; b=C1feOBIqIBMg0EOWPTXPRwLvAzFmXxBsW0AJ9tw2Nfaw4OeZmIDiIavIciDlD5bs8XPo4aYu02DWV/ZnfkxO5U8eKHReq5JPzXiMRU4Xh3lEb7mG4YSKVqPSx3fAz6kxkeWdmwshxsj8w/hRK2ZRAH+ZeRG6A5ywZtDuOqhRJZQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160171; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=GYlE0CVt2AjKwTUBmdJU6SEJXf+8f020xSVMTSYvRP8=; b=TJur4a9GT8LqY8ZVAG7aR8DV6tWWIVMeZo92jPF4D20dKPdWYEDg+uKJjHf2GVRwcHVoV8ekiAVjXvR91uRVb/rybJjm67tNBHO6FdWiJE06+yek7lIZSqMjdAO+ad5wTeBmQq9gcFjHARzgNx6RJuAqq1z6zJ8+yWR7c03nq50= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160171891814.482518736528; Tue, 10 Mar 2026 09:29:31 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvX-0003nj-S6; Tue, 10 Mar 2026 12:27:23 -0400 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 1vzzvS-0003WN-JH for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:18 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvO-0000z9-Hn for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:16 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-600-Twt1K90AMNOsjlm5-tPpXQ-1; Tue, 10 Mar 2026 12:27:12 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 6D93B180049D; Tue, 10 Mar 2026 16:27:11 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4689619560A6; Tue, 10 Mar 2026 16:27:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160033; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GYlE0CVt2AjKwTUBmdJU6SEJXf+8f020xSVMTSYvRP8=; b=CVGdtrOnoILWhtzwCtVHrfVeRIPtil+gDr6Dtfgzu+FKRk7Wz233ibV6vU/jpYaiiPPihv aqVwwpHE0EBltuzF6ufMPfCqntBp+qKq/I1J2kN09dB1gTy768Jk3TuwqVgEP5fPygwdtH oKDxmgpNDq1XND4m+XaPAUQP7SYPjrs= X-MC-Unique: Twt1K90AMNOsjlm5-tPpXQ-1 X-Mimecast-MFC-AGG-ID: Twt1K90AMNOsjlm5-tPpXQ_1773160031 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 25/28] iotests/308: Add multi-threading sanity test Date: Tue, 10 Mar 2026 17:26:19 +0100 Message-ID: <20260310162622.333137-26-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160173272158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek Run qemu-img bench on a simple multi-threaded FUSE export to test that it works. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-26-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/308 | 51 ++++++++++++++++++++++++++++++++++ tests/qemu-iotests/308.out | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index a83c6fc01fb..f4a06a522ef 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -441,6 +441,57 @@ $QEMU_IO -c 'read -P 0 0 64M' "$TEST_IMG" | _filter_qe= mu_io =20 _cleanup_test_img =20 +echo +echo '=3D=3D=3D Multi-threading =3D=3D=3D' + +# Just set up a null block device, export it (with multi-threading), and r= un +# qemu-img bench on it (to get parallel requests) + +_launch_qemu +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'driver': 'null-co', + 'node-name': 'null' + } }" \ + 'return' + +for id in iothread{0,1,2,3}; do + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'object-add', + 'arguments': { + 'qom-type': 'iothread', + 'id': '$id' + } }" \ + 'return' +done + +echo + +iothreads=3D"['iothread0', 'iothread1', 'iothread2', 'iothread3']" +fuse_export_add \ + 'export' \ + "'mountpoint': '$EXT_MP', 'iothread': $iothreads" \ + 'return' \ + 'null' + +echo +$QEMU_IMG bench -f raw "$EXT_MP" | + sed -e 's/[0-9.]\+ seconds/X.XXX seconds/' +echo + +fuse_export_del 'export' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'quit'}" \ + 'return' + +wait=3Dyes _cleanup_qemu + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index ebeaf64b486..580cc94e92f 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -217,4 +217,60 @@ read 67108864/67108864 bytes at offset 0 {"return": {}} read 67108864/67108864 bytes at offset 0 64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=3D=3D=3D Multi-threading =3D=3D=3D +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'blockdev-add', + 'arguments': { + 'driver': 'null-co', + 'node-name': 'null' + } } +{"return": {}} +{'execute': 'object-add', + 'arguments': { + 'qom-type': 'iothread', + 'id': 'iothread0' + } } +{"return": {}} +{'execute': 'object-add', + 'arguments': { + 'qom-type': 'iothread', + 'id': 'iothread1' + } } +{"return": {}} +{'execute': 'object-add', + 'arguments': { + 'qom-type': 'iothread', + 'id': 'iothread2' + } } +{"return": {}} +{'execute': 'object-add', + 'arguments': { + 'qom-type': 'iothread', + 'id': 'iothread3' + } } +{"return": {}} + +{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': 'export', + 'node-name': 'null', + 'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'iothread': ['iothre= ad0', 'iothread1', 'iothread2', 'iothread3'] + } } +{"return": {}} + +Sending 75000 read requests, 4096 bytes each, 64 in parallel (starting at = offset 0, step size 4096) +Run completed in X.XXX seconds. + +{'execute': 'block-export-del', + 'arguments': { + 'id': 'export' + } } +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +{'execute': 'quit'} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160233; cv=none; d=zohomail.com; s=zohoarc; b=awHsDX3Vyt0lUSuxP4wUzEkaQrQEbyoXR8I/c3r4ZF4YKsy21A+rGz+9+dIhiwFo/iMaD3E+h6xiFtMerkDcZtHgYSjZgYi5NT6S1SeyjPYU1QAEH9nhafiMdfYjxLfrFZXzuu2BoxM+Wo7T8C9q57XjloVDwifE524KHXPGt4s= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160233; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=dXevCX+TKSamsDo/RaI3tSNiKHhigt69wLKY0jDj1bk=; b=Tc4xCmqon/tUSNFIiVAYQR1PxOaL5hLWv8SgIBOtUz4/ATnoxXi6dvU7F2a56A+5ez7tVmWogc6VrHNKdO1WlqjFZ9EvOaTRZLM4+YMNKFeF3EgKix5OeDeIEB+rLab9wSNPrZ3IcNPQG0fmfk8XPSgIHNJpZc3l3TIn9HzsbVI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160233169791.0778790231001; Tue, 10 Mar 2026 09:30:33 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvX-0003mv-Rh; Tue, 10 Mar 2026 12:27:23 -0400 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 1vzzvU-0003Yq-TC for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:20 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvS-0000zk-3l for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:19 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-424-betGy7E2OeqhPiu1Fb74bg-1; Tue, 10 Mar 2026 12:27:14 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 09BE4180034E; Tue, 10 Mar 2026 16:27:13 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D8A9F19560A6; Tue, 10 Mar 2026 16:27:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160036; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dXevCX+TKSamsDo/RaI3tSNiKHhigt69wLKY0jDj1bk=; b=VGSrhszawmxgOo0Ux9It20M2YTtCAdBVgQnhdc3TBaS8eGTtlwRuVI6DUIvrzJ0PGIjo1F 5UVLxil/xlop/P3EGT/r5WZjErHXLzmKBj/3YjHA1tlURIfB4v0L7M7lojpekoflxzLo5T 6JzisEo7oFdS+oFI1/cunkoE9bDeHTo= X-MC-Unique: betGy7E2OeqhPiu1Fb74bg-1 X-Mimecast-MFC-AGG-ID: betGy7E2OeqhPiu1Fb74bg_1773160033 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 26/28] block/nfs: add support for libnfs v6 Date: Tue, 10 Mar 2026 17:26:20 +0100 Message-ID: <20260310162622.333137-27-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160235392154100 Content-Type: text/plain; charset="utf-8" From: Peter Lieven libnfs v6 added a new api structure for read and write requests. This effectively also adds zero copy read support for cases where the qiov coming from the block layer has only one vector. The .brdv_refresh_limits implementation is needed because libnfs v6 silently dropped support for splitting large read/write request into chunks. Signed-off-by: Ronnie Sahlberg Signed-off-by: Peter Lieven Message-ID: <20260306142840.72923-1-pl@dlhnet.de> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/nfs.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- meson.build | 2 +- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/block/nfs.c b/block/nfs.c index b78f4f86e85..53e267fa755 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -69,7 +69,9 @@ typedef struct NFSClient { typedef struct NFSRPC { BlockDriverState *bs; int ret; +#ifndef LIBNFS_API_V2 QEMUIOVector *iov; +#endif struct stat *st; Coroutine *co; NFSClient *client; @@ -237,6 +239,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, voi= d *data, NFSRPC *task =3D private_data; task->ret =3D ret; assert(!task->st); +#ifndef LIBNFS_API_V2 if (task->ret > 0 && task->iov) { if (task->ret <=3D task->iov->size) { qemu_iovec_from_buf(task->iov, 0, data, task->ret); @@ -244,6 +247,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, voi= d *data, task->ret =3D -EIO; } } +#endif if (task->ret < 0) { error_report("NFS Error: %s", nfs_get_error(nfs)); } @@ -266,13 +270,36 @@ static int coroutine_fn nfs_co_preadv(BlockDriverStat= e *bs, int64_t offset, { NFSClient *client =3D bs->opaque; NFSRPC task; + char *buf =3D NULL; + bool my_buffer =3D false; =20 nfs_co_init_task(bs, &task); - task.iov =3D iov; + +#ifdef LIBNFS_API_V2 + if (iov->niov !=3D 1) { + buf =3D g_try_malloc(bytes); + if (bytes && buf =3D=3D NULL) { + return -ENOMEM; + } + my_buffer =3D true; + } else { + buf =3D iov->iov[0].iov_base; + } +#endif =20 WITH_QEMU_LOCK_GUARD(&client->mutex) { +#ifdef LIBNFS_API_V2 + if (nfs_pread_async(client->context, client->fh, + buf, bytes, offset, + nfs_co_generic_cb, &task) !=3D 0) { +#else + task.iov =3D iov; if (nfs_pread_async(client->context, client->fh, offset, bytes, nfs_co_generic_cb, &task) !=3D = 0) { +#endif + if (my_buffer) { + g_free(buf); + } return -ENOMEM; } =20 @@ -280,6 +307,13 @@ static int coroutine_fn nfs_co_preadv(BlockDriverState= *bs, int64_t offset, } qemu_coroutine_yield(); =20 + if (my_buffer) { + if (task.ret > 0) { + qemu_iovec_from_buf(iov, 0, buf, task.ret); + } + g_free(buf); + } + if (task.ret < 0) { return task.ret; } @@ -315,9 +349,15 @@ static int coroutine_fn nfs_co_pwritev(BlockDriverStat= e *bs, int64_t offset, } =20 WITH_QEMU_LOCK_GUARD(&client->mutex) { +#ifdef LIBNFS_API_V2 + if (nfs_pwrite_async(client->context, client->fh, + buf, bytes, offset, + nfs_co_generic_cb, &task) !=3D 0) { +#else if (nfs_pwrite_async(client->context, client->fh, offset, bytes, buf, nfs_co_generic_cb, &task) !=3D 0) { +#endif if (my_buffer) { g_free(buf); } @@ -856,6 +896,13 @@ static void coroutine_fn nfs_co_invalidate_cache(Block= DriverState *bs, } #endif =20 +static void nfs_refresh_limits(BlockDriverState *bs, Error **errp) +{ + NFSClient *client =3D bs->opaque; + bs->bl.max_transfer =3D MIN((uint32_t)nfs_get_readmax(client->context), + (uint32_t)nfs_get_writemax(client->context)); +} + static const char *nfs_strong_runtime_opts[] =3D { "path", "user", @@ -893,6 +940,7 @@ static BlockDriver bdrv_nfs =3D { .bdrv_detach_aio_context =3D nfs_detach_aio_context, .bdrv_attach_aio_context =3D nfs_attach_aio_context, .bdrv_refresh_filename =3D nfs_refresh_filename, + .bdrv_refresh_limits =3D nfs_refresh_limits, .bdrv_dirname =3D nfs_dirname, =20 .strong_runtime_opts =3D nfs_strong_runtime_opts, diff --git a/meson.build b/meson.build index f45885f05a1..bb0d1d993a5 100644 --- a/meson.build +++ b/meson.build @@ -1157,7 +1157,7 @@ endif =20 libnfs =3D not_found if not get_option('libnfs').auto() or have_block - libnfs =3D dependency('libnfs', version: ['>=3D1.9.3', '<6.0.0'], + libnfs =3D dependency('libnfs', version: '>=3D1.9.3', required: get_option('libnfs'), method: 'pkg-config') endif --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160179; cv=none; d=zohomail.com; s=zohoarc; b=WlumZhVfTAxyEiGn+s/64Z9ZgT+m0/8lHqoqZBFYEvqVPIsVcBOd8F/7lx2uqzpGw7MU8TBUI4rTFyTWFno3MW69xRb9D0F/WOinJ1dQIjC9c+BM5HvMd61j3CXYH5axY+wV7z9T0iR2hNc18F3Z7TxwlYdCc2O29IVguWH8cuc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160179; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=BljQaDFUCxkFjbyZi0Hu5Rjj8PEQG8JiRAUI1zZAOyE=; b=KV6NIPxYO+zF/VKrLMgRXO18gImGU3kuso46+xBhEZAE0XsJmaQfO7zn6nTNpTniQ8cPTVV6z1SCw1UeuuHy2OTo3I5JwKEYBRzFRzMAevB6Z7q/6tW/wE2zV7ovAljJnA3j/RvoUCRYUqow+WHAvClDfoOkOxkwvpWPUfBuCv4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160179566158.89104209210313; Tue, 10 Mar 2026 09:29:39 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvZ-0003vK-QB; Tue, 10 Mar 2026 12:27:25 -0400 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 1vzzvU-0003Yt-To for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:20 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvS-0000zr-8E for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:20 -0400 Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-626-42AF2qBlOQyl_A4CzHApnA-1; Tue, 10 Mar 2026 12:27:15 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9AE5518002E7; Tue, 10 Mar 2026 16:27:14 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 733F819560A6; Tue, 10 Mar 2026 16:27:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160036; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BljQaDFUCxkFjbyZi0Hu5Rjj8PEQG8JiRAUI1zZAOyE=; b=jRgFkRfMJbIzHgyWGiaYySQoGf+akt9k72OEuBgQYoYqgysxhgNLOj5n7vlSTxBXErMaux 4Ef7stCYV4KtgJEt6iHLEEcq6v6ONHkQ9odpV24A92hlXfGg/5bOvq9d45cf8vsBitOwQe 7BSKvjgo/EHlBgcwik8xUoqJzSoSb1Q= X-MC-Unique: 42AF2qBlOQyl_A4CzHApnA-1 X-Mimecast-MFC-AGG-ID: 42AF2qBlOQyl_A4CzHApnA_1773160034 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 27/28] qapi: block: Refactor HTTP(s) common arguments Date: Tue, 10 Mar 2026 17:26:21 +0100 Message-ID: <20260310162622.333137-28-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160180380154100 Content-Type: text/plain; charset="utf-8" From: Antoine Damhet The HTTPs curl block driver is a superset of the HTTP driver, reflect that in the QAPI. Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: Antoine Damhet Message-ID: <20260227-fix-curl-v3-v3-2-eb8a4d88feef@scaleway.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qapi/block-core.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index da0b36a3751..8ba1fdc49d6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4600,23 +4600,14 @@ # Driver specific block device options for HTTPS connections over the # curl backend. URLs must start with "https://". # -# @cookie: List of cookies to set; format is "name1=3Dcontent1; -# name2=3Dcontent2;" as explained by CURLOPT_COOKIE(3). Defaults to -# no cookies. -# # @sslverify: Whether to verify the SSL certificate's validity # (defaults to true) # -# @cookie-secret: ID of a QCryptoSecret object providing the cookie -# data in a secure way. See @cookie for the format. (since 2.10) -# # Since: 2.9 ## { 'struct': 'BlockdevOptionsCurlHttps', - 'base': 'BlockdevOptionsCurlBase', - 'data': { '*cookie': 'str', - '*sslverify': 'bool', - '*cookie-secret': 'str'} } + 'base': 'BlockdevOptionsCurlHttp', + 'data': { '*sslverify': 'bool'} } =20 ## # @BlockdevOptionsCurlFtp: --=20 2.53.0 From nobody Wed Apr 8 02:50:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1773160194; cv=none; d=zohomail.com; s=zohoarc; b=muTaI9WicS0rAxDOX6GBM9GPCzx/PrWmC8EKSWnPRSJsKS+qpY6BPrGnC9nqQJGypgW8rNm9y8/NBdYZGMHH0EUfVHsTNgx1TWqafzyuC76w1YVAdLRCgxA2XDFs/826pWH/Kz7pIVlijIx1LAs5/9E9WqgM5zMLb71SQfZJNS8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773160194; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=3NYvw0OxBs5HdokP5YldS9J+T2s8QfdGmfB9yCRG9hQ=; b=Blv1Wh8kcowP+vlUhjtif4uEK76gF8Jmadx3n7fgEWX/J1Kyk7TYX8yF6kSu+y24ZEZ5BU4CVkDHQmnzSogKFwkxOxEKRv3hyfAl+laPSwTPUKPfZRGB6IqJmojuTErRdBVrOtCNtg7ErdQBZZz3K+67a3Zkv0QNxOlf3RrNZHY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773160194417621.3689955078261; Tue, 10 Mar 2026 09:29:54 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vzzvZ-0003tO-9c; Tue, 10 Mar 2026 12:27:25 -0400 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 1vzzvW-0003h4-Kx for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:22 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vzzvT-00010E-Rq for qemu-devel@nongnu.org; Tue, 10 Mar 2026 12:27:22 -0400 Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-416-Ime2abjDPvCAHVFPOXL4UA-1; Tue, 10 Mar 2026 12:27:17 -0400 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7A6CF19560B3; Tue, 10 Mar 2026 16:27:16 +0000 (UTC) Received: from merkur.fritz.box (unknown [10.45.224.112]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 10E9819560A6; Tue, 10 Mar 2026 16:27:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773160038; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3NYvw0OxBs5HdokP5YldS9J+T2s8QfdGmfB9yCRG9hQ=; b=ahiULIqrdyrPRwfx+vXYZV8GtaeGtggzzJRdWkSg6mKnK5jmvtFqUjyUBoSQQ4KQ5BvI+8 gcwwPB2lAVwNh9OHG/vLlaDtrRWee7QQOnGzTsE4o6QrKNdQxKeDdnSziedD3CsB/XD0j7 3VEVNdLSYXhwff/pFggayDOExRLOVm4= X-MC-Unique: Ime2abjDPvCAHVFPOXL4UA-1 X-Mimecast-MFC-AGG-ID: Ime2abjDPvCAHVFPOXL4UA_1773160036 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [PULL 28/28] block/curl: add support for S3 presigned URLs Date: Tue, 10 Mar 2026 17:26:22 +0100 Message-ID: <20260310162622.333137-29-kwolf@redhat.com> In-Reply-To: <20260310162622.333137-1-kwolf@redhat.com> References: <20260310162622.333137-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 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=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -3 X-Spam_score: -0.4 X-Spam_bar: / X-Spam_report: (-0.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development 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-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1773160195475158500 Content-Type: text/plain; charset="utf-8" From: Antoine Damhet S3 presigned URLs are signed for a specific HTTP method (typically GET for our use cases). The curl block driver currently issues a HEAD request to discover the web server features and the file size, which fails with 'HTTP 403' (forbidden). Add a 'force-range' option that skips the HEAD request and instead issues a minimal GET request (querying 1 byte from the server) to extract the file size from the 'Content-Range' response header. To achieve this the 'curl_header_cb' is redesigned to generically parse HTTP headers. $ $QEMU -drive driver=3Dhttps,\ 'url=3Dhttps://s3.example.com/some.img?X-Amz-Security-Token=3D= XXX', force-range=3Dtrue Enabling the 'force-range' option without the web server specified with @url supporting it might cause the server to respond successfully with 'HTTP 200' and attempt to send the whole file body. With the 'CURLOPT_NOBODY' option set the libcurl will skip reading after the headers and close the connection. QEMU still gracefully detects the missing feature. This might waste a small number of TCP packets but is otherwise transparent to the user. Acked-by: Markus Armbruster Signed-off-by: Antoine Damhet Message-ID: <20260227-fix-curl-v3-v3-3-eb8a4d88feef@scaleway.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qapi/block-core.json | 8 +- docs/system/device-url-syntax.rst.inc | 6 ++ block/curl.c | 104 ++++++++++++++++++-------- block/trace-events | 1 + 4 files changed, 85 insertions(+), 34 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 8ba1fdc49d6..f8d446b3d6e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4587,12 +4587,18 @@ # @cookie-secret: ID of a QCryptoSecret object providing the cookie # data in a secure way. See @cookie for the format. (since 2.10) # +# @force-range: Don't issue a HEAD HTTP request to discover if the +# http server supports range requests and rely only on GET +# requests. This is especially useful for S3 presigned URLs where +# HEAD requests are unauthorized. (default: false; since 11.0) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsCurlHttp', 'base': 'BlockdevOptionsCurlBase', 'data': { '*cookie': 'str', - '*cookie-secret': 'str'} } + '*cookie-secret': 'str', + '*force-range': 'bool'} } =20 ## # @BlockdevOptionsCurlHttps: diff --git a/docs/system/device-url-syntax.rst.inc b/docs/system/device-url= -syntax.rst.inc index aae65d138c0..996ce5418ff 100644 --- a/docs/system/device-url-syntax.rst.inc +++ b/docs/system/device-url-syntax.rst.inc @@ -179,6 +179,12 @@ These are specified using a special URL syntax. get the size of the image to be downloaded. If not set, the default timeout of 5 seconds is used. =20 + ``force-range`` + Don't issue a HEAD HTTP request to discover if the http server + server supports range requests and rely only on GET requests. This + is especially useful for S3 presigned URLs where HEAD requests + are unauthorized. It defaults to 'false'. + Note that when passing options to qemu explicitly, ``driver`` is the value of . =20 diff --git a/block/curl.c b/block/curl.c index 6dccf002564..66aecfb20ec 100644 --- a/block/curl.c +++ b/block/curl.c @@ -62,10 +62,12 @@ #define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret" #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username" #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret" +#define CURL_BLOCK_OPT_FORCE_RANGE "force-range" =20 #define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024) #define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true #define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5 +#define CURL_BLOCK_OPT_FORCE_RANGE_DEFAULT false =20 struct BDRVCURLState; struct CURLState; @@ -206,27 +208,33 @@ static size_t curl_header_cb(void *ptr, size_t size, = size_t nmemb, void *opaque) { BDRVCURLState *s =3D opaque; size_t realsize =3D size * nmemb; - const char *p =3D ptr; - const char *end =3D p + realsize; - const char *t =3D "accept-ranges : bytes "; /* A lowercase template */ + g_autofree char *header =3D g_strstrip(g_strndup(ptr, realsize)); + char *val =3D strchr(header, ':'); =20 - /* check if header matches the "t" template */ - for (;;) { - if (*t =3D=3D ' ') { /* space in t matches any amount of isspace i= n p */ - if (p < end && g_ascii_isspace(*p)) { - ++p; - } else { - ++t; - } - } else if (*t && p < end && *t =3D=3D g_ascii_tolower(*p)) { - ++p, ++t; - } else { - break; - } + if (!val) { + return realsize; } =20 - if (!*t && p =3D=3D end) { /* if we managed to reach ends of both stri= ngs */ - s->accept_range =3D true; + *val++ =3D '\0'; + g_strchomp(header); + while (g_ascii_isspace(*val)) { + ++val; + } + + trace_curl_header_cb(header, val); + + if (!g_ascii_strcasecmp(header, "accept-ranges")) { + if (!g_ascii_strcasecmp(val, "bytes")) { + s->accept_range =3D true; + } + } else if (!g_ascii_strcasecmp(header, "Content-Range")) { + /* Content-Range fmt is `bytes begin-end/full_size` */ + val =3D strchr(val, '/'); + if (val) { + if (qemu_strtou64(val + 1, NULL, 10, &s->len) < 0) { + s->len =3D UINT64_MAX; + } + } } =20 return realsize; @@ -668,6 +676,11 @@ static QemuOptsList runtime_opts =3D { .type =3D QEMU_OPT_STRING, .help =3D "ID of secret used as password for HTTP proxy auth", }, + { + .name =3D CURL_BLOCK_OPT_FORCE_RANGE, + .type =3D QEMU_OPT_BOOL, + .help =3D "Assume HTTP range requests are supported", + }, { /* end of list */ } }, }; @@ -690,6 +703,7 @@ static int curl_open(BlockDriverState *bs, QDict *optio= ns, int flags, #endif const char *secretid; const char *protocol_delimiter; + bool force_range; int ret; =20 bdrv_graph_rdlock_main_loop(); @@ -807,35 +821,56 @@ static int curl_open(BlockDriverState *bs, QDict *opt= ions, int flags, } =20 s->accept_range =3D false; + s->len =3D UINT64_MAX; + force_range =3D qemu_opt_get_bool(opts, CURL_BLOCK_OPT_FORCE_RANGE, + CURL_BLOCK_OPT_FORCE_RANGE_DEFAULT); + /* + * When minimal CURL will be bumped to `7.83`, the header callback + m= anual + * parsing can be replaced by `curl_easy_header` calls + */ if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1L) || curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_= cb) || curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) { - pstrcpy(state->errmsg, CURL_ERROR_SIZE, - "curl library initialization failed."); - goto out; + goto out_init; + } + if (force_range) { + if (curl_easy_setopt(state->curl, CURLOPT_CUSTOMREQUEST, "GET") || + curl_easy_setopt(state->curl, CURLOPT_RANGE, "0-0")) { + goto out_init; + } } + if (curl_easy_perform(state->curl)) goto out; - /* CURL 7.55.0 deprecates CURLINFO_CONTENT_LENGTH_DOWNLOAD in favour of - * the *_T version which returns a more sensible type for content leng= th. - */ + + if (!force_range) { + /* + * CURL 7.55.0 deprecates CURLINFO_CONTENT_LENGTH_DOWNLOAD in favo= ur of + * the *_T version which returns a more sensible type for content + * length. + */ #if LIBCURL_VERSION_NUM >=3D 0x073700 - if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,= &cl)) { - goto out; - } + if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOA= D_T, + &cl)) { + goto out; + } #else - if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &= cl)) { - goto out; - } + if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOA= D, + &cl)) { + goto out; + } #endif - if (cl < 0) { + if (cl >=3D 0) { + s->len =3D cl; + } + } + + if (s->len =3D=3D UINT64_MAX) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, "Server didn't report file size."); goto out; } =20 - s->len =3D cl; - if ((!strncasecmp(s->url, "http://", strlen("http://")) || !strncasecmp(s->url, "https://", strlen("https://"))) && !s->accept_range) { @@ -856,6 +891,9 @@ static int curl_open(BlockDriverState *bs, QDict *optio= ns, int flags, qemu_opts_del(opts); return 0; =20 +out_init: + pstrcpy(state->errmsg, CURL_ERROR_SIZE, + "curl library initialization failed."); out: error_setg(errp, "CURL: Error opening file: %s", state->errmsg); curl_easy_cleanup(state->curl); diff --git a/block/trace-events b/block/trace-events index c9b4736ff88..d170fc96f15 100644 --- a/block/trace-events +++ b/block/trace-events @@ -191,6 +191,7 @@ ssh_server_status(int status) "server status=3D%d" curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" curl_sock_cb(int action, int fd) "sock action %d on fd %d" curl_read_cb(size_t realsize) "just reading %zu bytes" +curl_header_cb(const char *key, const char *val) "looking at %s: %s" curl_open(const char *file) "opening %s" curl_open_size(uint64_t size) "size =3D %" PRIu64 curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "read= ing %" PRIu64 " at %" PRIu64 " (%s)" --=20 2.53.0