From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4AEBE51C056 for ; Thu, 1 Oct 2026 15:09:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790867391; cv=none; b=CP0/AtMH0gY6aw0npUHmdTebNeIJFvPvsRGEovUkbaNe+hneUQFJFwytN2spUIvDPiQnp23/0BPEXEHvm5L+3ulwHf4WciwB4UUV0L9VPqL9MM0hNBppBeapuIxyBWQ7RU36mXdSS3aXxFhpivvs5BGf2enEq/m19dcAbxpHn4I= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790867391; c=relaxed/simple; bh=bl0rPBSBFKJXFFcSND+2Sx6IhW2Gei6pJ6J5QRVVPII=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:content-type; b=oEIQWDY2VvKOi/OBMsrpNxWTp3pyuYHsoZnUQvTPm+1Q6/RdON7NO6iLXg8OhwSFedotEFpETGjgii3joA6wZ7afxi83KTIFouagK6NvDogzrkNskTHUFlmQWimi6HvChEB837/XQuvYVAWcJKuMH3mpCfkR6dVI87MOZ6R+nkY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=gAWr4mQr; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="gAWr4mQr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1790867388; 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=J5yZLNrSRV64eauo69wxs15T8YxD6m1PPFEn9wYcIOY=; b=gAWr4mQrQiM9xb8iZF3aI5onEmhYPJUuV3I3znApdvq4PALS/E6A9FscXBEY3etf5U8unm ez0dTOj8HcH8/lRuiFrX4xsW/wNliHmjXspq0r0Sh9FhW8mL0PgChW+MkL5H1wPzfafCYU gdZcvCqrhVzGVA/R/ZrD1Ra75zjui2M= Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-571-mPKtaBQXNoeXAo3duwJqOQ-1; Thu, 01 Oct 2026 11:09:44 -0400 X-MC-Unique: mPKtaBQXNoeXAo3duwJqOQ-1 X-Mimecast-MFC-AGG-ID: mPKtaBQXNoeXAo3duwJqOQ_1790867384 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-c29959eb71fso672435966b.1 for ; Thu, 01 Oct 2026 08:09:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790867384; x=1791472184; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=J5yZLNrSRV64eauo69wxs15T8YxD6m1PPFEn9wYcIOY=; b=SHNcrBApe6UM4NyvU4aVrq427nXbgwkp7rzwLmUaxl2plpZm6/eRiTEGQKxKw5DbGP rZ8XTRnm1MhFvJY1uUqzeCoNGdheLIrJYib0Zh5T85++fUlBbAEUK0SAZgpksrLeeoAy F+d8yNvay8KpgoODpV3XxHT6hnw/LkqoMV0JBbosCIgu6BE2/VVSiBfdEnwZX6YDBvjR MkgFjyyeosHnBQxdeyQBoLpB8bPmni9QeKCSUSs2uS6rspcV4q8HC/5WZcI10SbRmWl1 Zbd8KN1vmENU1GzbslVL7+Ws33d1a8Q8KM06+AgYbGwfm+mC1nPN0AFtgKqQV1nVraw/ y/hA== X-Gm-Message-State: AFuF++nL9szMSuA3V0HfXFwklT3TEFVJYMJTPIi5tu+3/pXBnFz/uJOE GaVhQT6Um+pSI2754E4Po072dHoNsdZMnrywWGJ1ovYEZ/7IiDq2eDGz4SGLHqZnbxcfNIfcCLd EvzAfVG0glVMzGvKzb6bCHOpapU2xQVYkzJzXZd7tZzLRNOyJdIx8i0N5xsyTh1YE7qJfTTdW8y zcNrTHSWdDDW8c+DoLDQj1hRTzDjKCtThcDqSRr0eY2ehBGCo= X-Gm-Gg: AYBFou1B4wo40eRM5/B3VosQ6GheDRHLl1Un0Ru8ln/E06vXD9xC9qwH/LgCYPUluQZ SDUI+muJHGbZa6e4TCazjwywjb4v+ecpCKq+nkJaiJQ+phRubcuw6TuOvXnKExuLRI7wZYiZhDu CMIo2KDhBZbXa2RU9r7ZZtuRG2HbFwMR+B4Gxp3ZDjkIztsqKFuQNtyjUuoYCvT+y1HFs2I3Iik p7Fq+CYddZ4JQ+nPSpbzSt1boG/JeTMsveE1enSivbB19N7E6zvNJi76rH1u9y1p4xLsshSMUP6 aKVg2yZOQDeX2/vpLKZLc1fFeoU29taXrAw1uGNfkWUUakuB3/U5XHL1zAKNlo/j/md/sin/SR3 rWybW5YBCoQVXGX6l9BRwV9vzo7EqKu/EwI6LtrFBIGd0ypmrQCEjuE0h01w= X-Received: by 2002:a17:906:4784:b0:c2d:ced1:4615 with SMTP id a640c23a62f3a-c2e23caa0cbmr418531566b.8.1790867383636; Thu, 01 Oct 2026 08:09:43 -0700 (PDT) X-Received: by 2002:a17:906:4784:b0:c2d:ced1:4615 with SMTP id a640c23a62f3a-c2e23caa0cbmr418528466b.8.1790867383069; Thu, 01 Oct 2026 08:09:43 -0700 (PDT) Received: from maszat.piliscsaba.szeredi.hu (193-226-245-188.pool.digikabel.hu. [193.226.245.188]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c2e31d86372sm177735166b.56.2026.10.01.08.09.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 01 Oct 2026 08:09:42 -0700 (PDT) From: Miklos Szeredi To: fuse-devel@lists.linux.dev Cc: John Groves , Amir Goldstein , "Darrick J . Wong" , Vishal Verma , Dave Jiang , Alison Schofield , nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org Subject: [PATCH v2 2/8] fuse: add helpers for EIO return value with kernel message Date: Thu, 1 Oct 2026 17:07:20 +0200 Message-ID: <20261001150935.655979-3-mszeredi@redhat.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20261001150935.655979-1-mszeredi@redhat.com> References: <20261001150935.655979-1-mszeredi@redhat.com> Precedence: bulk X-Mailing-List: fuse-devel@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: norltQlUXxt0fQJP9bSIPXcitaoVJp2bs3eVP-YVTzo_1790867384 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true Conditions are triggered by a buggy fuse server will result in -EIO which is hard to interpret without any context. Add helpers that print a short message to the kernel log as well as returning the error value. This serves a dual purpose: - documents the error condition inside the code - allows the implementor of the fuse server to get the context for the error Some debug messages are removed in favor of this. This also changes the mmap return value from -ETXTBSY to -ENODEV if a passthrough open raced with the prior check. This makes both cases return -ENODEV if the file is in passthrough mode. Additionally change the return value of fuse_inode_uncached_io_start() and fuse_file_cached_io_open() from an error value to a bool (true on success) as the error value is translated anyway. Currently these use the pr_notice_once() variant. Possibly should be changed to a ratelimit of e.g. once per minute. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 8 ++--- fs/fuse/fuse_i.h | 6 ++-- fs/fuse/iomode.c | 74 ++++++++++++++++--------------------------- fs/fuse/passthrough.c | 19 ++++------- 4 files changed, 42 insertions(+), 65 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 6d707f2b3bff..92976906ab05 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1478,7 +1478,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from, * have raced, so check it again. */ if (fuse_io_past_eof(iocb, from) || - fuse_inode_uncached_io_start(fi, NULL) != 0) { + !fuse_inode_uncached_io_start(fi, NULL)) { inode_unlock_shared(inode); inode_lock(inode); *exclusive = true; @@ -2415,7 +2415,6 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fm->fc; struct inode *inode = file_inode(file); - int rc; /* DAX mmap is superior to direct_io mmap */ if (FUSE_IS_VDAX(inode)) @@ -2457,9 +2456,8 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) * After first mmap, the inode stays in caching io mode until * the direct_io file release. */ - rc = fuse_file_cached_io_open(inode, ff); - if (rc) - return rc; + if (!fuse_file_cached_io_open(inode, ff)) + return -ENODEV; } if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8546855386b5..3dd4dff24c50 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -11,6 +11,8 @@ # define pr_fmt(fmt) "fuse: " fmt #endif +#define fuse_EIO(fmt, ...) (pr_notice_once("%s: " fmt "\n", __func__, ##__VA_ARGS__), -EIO) + #include "args.h" #include #include @@ -1251,8 +1253,8 @@ int fuse_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct file_kattr *fa); /* iomode.c */ -int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff); -int fuse_inode_uncached_io_start(struct fuse_inode *fi, +bool fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff); +bool fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb); void fuse_inode_uncached_io_end(struct fuse_inode *fi); diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index 79637c09e883..1a10bc65fb31 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -27,15 +27,15 @@ static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi) * Blocks new parallel dio writes and waits for the in-progress parallel dio * writes to complete. */ -int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) +bool fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) { struct fuse_inode *fi = get_fuse_inode(inode); /* There are no io modes if server does not implement open */ if (!ff->args) - return 0; + return true; - spin_lock(&fi->lock); + guard(spinlock)(&fi->lock); /* * Setting the bit advises new direct-io writes to use an exclusive * lock - without it the wait below might be forever. @@ -53,8 +53,7 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) */ if (fuse_inode_backing(fi)) { clear_bit(FUSE_I_CACHE_IO_MODE, &fi->state); - spin_unlock(&fi->lock); - return -ETXTBSY; + return false; } WARN_ON(ff->iomode == IOM_UNCACHED); @@ -64,8 +63,7 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) set_bit(FUSE_I_CACHE_IO_MODE, &fi->state); fi->iocachectr++; } - spin_unlock(&fi->lock); - return 0; + return true; } static void fuse_file_cached_io_release(struct fuse_file *ff, @@ -82,22 +80,19 @@ static void fuse_file_cached_io_release(struct fuse_file *ff, } /* Start strictly uncached io mode where cache access is not allowed */ -int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb) +bool fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb) { struct fuse_backing *oldfb; - int err = 0; - spin_lock(&fi->lock); + guard(spinlock)(&fi->lock); /* deny conflicting backing files on same fuse inode */ oldfb = fuse_inode_backing(fi); - if (fb && oldfb && oldfb != fb) { - err = -EBUSY; - goto unlock; - } - if (fi->iocachectr > 0) { - err = -ETXTBSY; - goto unlock; - } + if (fb && oldfb && oldfb != fb) + return false; + + if (fi->iocachectr > 0) + return false; + fi->iocachectr--; /* fuse inode holds a single refcount of backing file */ @@ -107,9 +102,7 @@ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb) } else { fuse_backing_put(fb); } -unlock: - spin_unlock(&fi->lock); - return err; + return true; } /* Takes uncached_io inode mode reference to be dropped on file release */ @@ -118,11 +111,9 @@ static int fuse_file_uncached_io_open(struct inode *inode, struct fuse_backing *fb) { struct fuse_inode *fi = get_fuse_inode(inode); - int err; - err = fuse_inode_uncached_io_start(fi, fb); - if (err) - return err; + if (!fuse_inode_uncached_io_start(fi, fb)) + return fuse_EIO("failed to start uncached I/O"); WARN_ON(ff->iomode != IOM_NONE); ff->iomode = IOM_UNCACHED; @@ -173,9 +164,11 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file) int err; /* Check allowed conditions for file open in passthrough mode */ - if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough || - (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK)) - return -EINVAL; + if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough) + return fuse_EIO("passthrough not enabled"); + + if (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK) + return fuse_EIO("conflicting open flags"); fb = fuse_passthrough_open(file, ff->args->open_outarg.backing_id); if (IS_ERR(fb)) @@ -208,11 +201,12 @@ int fuse_file_io_open(struct file *file, struct inode *inode) /* * Server is expected to use FOPEN_PASSTHROUGH for all opens of an inode - * which is already open for passthrough. + * which is already open for passthrough. Using incorrect open mode is + * a server mistake, which results in user visible failure of open() + * with EIO error. */ - err = -EINVAL; if (fuse_inode_backing(fi) && !(ff->open_flags & FOPEN_PASSTHROUGH)) - goto fail; + return fuse_EIO("FOPEN_PASSTHROUGH expected"); /* * FOPEN_PARALLEL_DIRECT_WRITES requires FOPEN_DIRECT_IO. @@ -234,22 +228,10 @@ int fuse_file_io_open(struct file *file, struct inode *inode) if (ff->open_flags & FOPEN_PASSTHROUGH) err = fuse_file_passthrough_open(inode, file); - else - err = fuse_file_cached_io_open(inode, ff); - if (err) - goto fail; + else if (!fuse_file_cached_io_open(inode, ff)) + err = fuse_EIO("conflicting passthrough open"); - return 0; - -fail: - pr_debug("failed to open file in requested io mode (open_flags=0x%x, err=%i).\n", - ff->open_flags, err); - /* - * The file open mode determines the inode io mode. - * Using incorrect open mode is a server mistake, which results in - * user visible failure of open() with EIO error. - */ - return -EIO; + return err; } /* No more pending io and no new io possible to inode via open/mmapped file */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index b43d3e0f7081..489838462d1e 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -159,34 +159,29 @@ struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id) struct fuse_conn *fc = ff->fm->fc; struct fuse_backing *fb = NULL; struct file *backing_file; - int err; - err = -EINVAL; if (backing_id <= 0) - goto out; + return ERR_PTR(fuse_EIO("invalid backing_id")); - err = -ENOENT; fb = fuse_backing_lookup(fc, backing_id); if (!fb) - goto out; + return ERR_PTR(fuse_EIO("backing not found")); /* Allocate backing file per fuse file to store fuse path */ backing_file = backing_file_open(file, file->f_flags, &fb->file->f_path, fb->cred); - err = PTR_ERR(backing_file); if (IS_ERR(backing_file)) { fuse_backing_put(fb); - goto out; + return ERR_PTR(fuse_EIO("failed to open backing file (%ld)", PTR_ERR(backing_file))); } - err = 0; ff->passthrough = backing_file; ff->cred = get_cred(fb->cred); -out: - pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p, err=%i\n", __func__, - backing_id, fb, ff->passthrough, err); - return err ? ERR_PTR(err) : fb; + pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p\n", __func__, + backing_id, fb, ff->passthrough); + + return fb; } void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb) -- 2.54.0