From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 91A2C44D015 for ; Tue, 28 Apr 2026 13:18:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382329; cv=none; b=YNkL//z3w2m4UkIvXT0YfYs4WxX+ZAsVhT8dWC45fjyOXZ9EYZlY0K/t6jsD5KykcmG23EW0rHUyFdmGq8bLbWnaxZ8INTUAFHbYsci/VYLU0IWEzVR90P2GxrrgiJVxFzSnjaLgsDDhZ/fzeT4LVEd/iZVbsD9k39hGxJTHk40= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382329; c=relaxed/simple; bh=PCEywMo+0ucWp3/WX+0dExm5VhO3M/QAe8pTFnnhHEE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LulmC5K/eTyxRdzffwtBR0lBuFPLutTYqyq8sMGPEOiMZQvuJoOuo6fweIbFatQD2PXYZwDHssD+/wci9BACFvvfEXhfT7L2Sp59yGl173VP5O0x5Nd2i4nd6OqiEEoG/+P0WqNZOJZwvf56AhBBj1PHQ5OMsddzIlwANxJTXZ8= 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=CMfoZw+G; arc=none smtp.client-ip=170.10.133.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="CMfoZw+G" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382325; 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=Uhi8kKMsF6kc+1iDdDV2ShwjmP4JKPaFHP00k+w/m/Q=; b=CMfoZw+Gjv2F4ZAlJVCyaL/56mQJ2HiX5PnbAn1m3a5JWxWDSDQ3ji20Fq+kt38NxLigJO 4+moM0cz5h1bAGTkYpvY3uAMR1UkV2fkm8eXHNTkYd9QlHm3gteOUQ5jNV4tOxLQ97P/Sa tjUWYoKPX5uKtqor7a5ubU4Cn2dangA= 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-122-4-4frwHBOLyANWMlho6SWw-1; Tue, 28 Apr 2026 09:18:40 -0400 X-MC-Unique: 4-4frwHBOLyANWMlho6SWw-1 X-Mimecast-MFC-AGG-ID: 4-4frwHBOLyANWMlho6SWw_1777382319 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 8774E191FE8F; Tue, 28 Apr 2026 13:18:07 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 81D681800348; Tue, 28 Apr 2026 13:18:04 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 01/24] netfs: Fix cancellation of a DIO and single read subrequests Date: Tue, 28 Apr 2026 14:17:31 +0100 Message-ID: <20260428131756.922303-2-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" When the preparation of a new subrequest for a read fails, if the subrequest has already been added to the stream->subrequests list, it can't simply be put and abandoned as the collector may see it. Also, if it hasn't been queued yet, it has two outstanding refs that both need to be put. Both DIO read and single-read dispatch fail at this; further, both differ in the order they do things to the way buffered read works. Fix cancellation of both DIO-read and single-read subrequests that failed preparation by the following steps: (1) Harmonise all three reads (buffered, dio, single) to queue the subreq before prepping it. (2) Make all three call netfs_queue_read() to do the queuing. (3) Set NETFS_RREQ_ALL_QUEUED independently of the queuing as we don't know the length of the subreq at this point. (4) In all cases, set the error and NETFS_SREQ_FAILED flag on the subreq and then call netfs_read_subreq_terminated() to deal with it. This will pass responsibility off to the collector for dealing with it. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use o= ne work item") Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 33 ++++++++++++------------------- fs/netfs/direct_read.c | 42 +++++++++++++--------------------------- fs/netfs/internal.h | 3 +++ fs/netfs/read_collect.c | 11 +++++++++++ fs/netfs/read_single.c | 23 ++++++++++------------ 5 files changed, 49 insertions(+), 63 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index a8c0d86118c5..286c93fc681e 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -156,9 +156,8 @@ static void netfs_read_cache_to_pagecache(struct netfs_= io_request *rreq, netfs_cache_read_terminated, subreq); } =20 -static void netfs_queue_read(struct netfs_io_request *rreq, - struct netfs_io_subrequest *subreq, - bool last_subreq) +void netfs_queue_read(struct netfs_io_request *rreq, + struct netfs_io_subrequest *subreq) { struct netfs_io_stream *stream =3D &rreq->io_streams[0]; =20 @@ -178,11 +177,6 @@ static void netfs_queue_read(struct netfs_io_request *= rreq, } } =20 - if (last_subreq) { - smp_wmb(); /* Write lists before ALL_QUEUED. */ - set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); - } - spin_unlock(&rreq->lock); } =20 @@ -233,6 +227,8 @@ static void netfs_read_to_pagecache(struct netfs_io_req= uest *rreq, subreq->start =3D start; subreq->len =3D size; =20 + netfs_queue_read(rreq, subreq); + source =3D netfs_cache_prepare_read(rreq, subreq, rreq->i_size); subreq->source =3D source; if (source =3D=3D NETFS_DOWNLOAD_FROM_SERVER) { @@ -253,6 +249,7 @@ static void netfs_read_to_pagecache(struct netfs_io_req= uest *rreq, rreq->debug_id, subreq->debug_index, subreq->len, size, subreq->start, ictx->zero_point, rreq->i_size); + netfs_cancel_read(subreq, ret); break; } subreq->len =3D len; @@ -261,12 +258,7 @@ static void netfs_read_to_pagecache(struct netfs_io_re= quest *rreq, if (rreq->netfs_ops->prepare_read) { ret =3D rreq->netfs_ops->prepare_read(subreq); if (ret < 0) { - subreq->error =3D ret; - /* Not queued - release both refs. */ - netfs_put_subrequest(subreq, - netfs_sreq_trace_put_cancel); - netfs_put_subrequest(subreq, - netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); break; } trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); @@ -295,17 +287,16 @@ static void netfs_read_to_pagecache(struct netfs_io_r= equest *rreq, slice =3D netfs_prepare_read_iterator(subreq, ractl); if (slice < 0) { ret =3D slice; - subreq->error =3D ret; - trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); - /* Not queued - release both refs. */ - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); break; } - size -=3D slice; start +=3D slice; + size -=3D slice; + if (size <=3D 0) { + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + } =20 - netfs_queue_read(rreq, subreq, size <=3D 0); netfs_issue_read(rreq, subreq); cond_resched(); } while (size > 0); diff --git a/fs/netfs/direct_read.c b/fs/netfs/direct_read.c index f72e6da88cca..6a8fb0d55e04 100644 --- a/fs/netfs/direct_read.c +++ b/fs/netfs/direct_read.c @@ -45,12 +45,11 @@ static void netfs_prepare_dio_read_iterator(struct netf= s_io_subrequest *subreq) * Perform a read to a buffer from the server, slicing up the region to be= read * according to the network rsize. */ -static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) +static void netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) { - struct netfs_io_stream *stream =3D &rreq->io_streams[0]; unsigned long long start =3D rreq->start; ssize_t size =3D rreq->len; - int ret =3D 0; + int ret; =20 do { struct netfs_io_subrequest *subreq; @@ -58,7 +57,10 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_= io_request *rreq) =20 subreq =3D netfs_alloc_subrequest(rreq); if (!subreq) { - ret =3D -ENOMEM; + /* Stash the error in the request if there's not + * already an error set. + */ + cmpxchg(&rreq->error, 0, -ENOMEM); break; } =20 @@ -66,25 +68,13 @@ static int netfs_dispatch_unbuffered_reads(struct netfs= _io_request *rreq) subreq->start =3D start; subreq->len =3D size; =20 - __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); - - spin_lock(&rreq->lock); - list_add_tail(&subreq->rreq_link, &stream->subrequests); - if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { - if (!stream->active) { - stream->collected_to =3D subreq->start; - /* Store list pointers before active flag */ - smp_store_release(&stream->active, true); - } - } - trace_netfs_sreq(subreq, netfs_sreq_trace_added); - spin_unlock(&rreq->lock); + netfs_queue_read(rreq, subreq); =20 netfs_stat(&netfs_n_rh_download); if (rreq->netfs_ops->prepare_read) { ret =3D rreq->netfs_ops->prepare_read(subreq); if (ret < 0) { - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); break; } } @@ -113,8 +103,6 @@ static int netfs_dispatch_unbuffered_reads(struct netfs= _io_request *rreq) set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); netfs_wake_collector(rreq); } - - return ret; } =20 /* @@ -137,21 +125,17 @@ static ssize_t netfs_unbuffered_read(struct netfs_io_= request *rreq, bool sync) // TODO: Use bounce buffer if requested =20 inode_dio_begin(rreq->inode); + netfs_dispatch_unbuffered_reads(rreq); =20 - ret =3D netfs_dispatch_unbuffered_reads(rreq); - - if (!rreq->submitted) { - netfs_put_request(rreq, netfs_rreq_trace_put_no_submit); - inode_dio_end(rreq->inode); - ret =3D 0; - goto out; - } + /* The collector will get run, even if we don't manage to submit any + * subreqs, so we shouldn't call inode_dio_end() here. + */ =20 if (sync) ret =3D netfs_wait_for_read(rreq); else ret =3D -EIOCBQUEUED; -out: + _leave(" =3D %zd", ret); return ret; } diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index d436e20d3418..645996ecfc80 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -23,6 +23,8 @@ /* * buffered_read.c */ +void netfs_queue_read(struct netfs_io_request *rreq, + struct netfs_io_subrequest *subreq); void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); int netfs_prefetch_for_write(struct file *file, struct folio *folio, size_t offset, size_t len); @@ -108,6 +110,7 @@ static inline void netfs_see_subrequest(struct netfs_io= _subrequest *subreq, */ bool netfs_read_collection(struct netfs_io_request *rreq); void netfs_read_collection_worker(struct work_struct *work); +void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error); void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); =20 /* diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index e5f6665b3341..d2d902f46627 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -575,6 +575,17 @@ void netfs_read_subreq_terminated(struct netfs_io_subr= equest *subreq) } EXPORT_SYMBOL(netfs_read_subreq_terminated); =20 +/* + * Cancel a read subrequest due to preparation failure. + */ +void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error) +{ + trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); + subreq->error =3D error; + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + netfs_read_subreq_terminated(subreq); +} + /* * Handle termination of a read from the cache. */ diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c index d0e23bc42445..8833550d2eb6 100644 --- a/fs/netfs/read_single.c +++ b/fs/netfs/read_single.c @@ -89,7 +89,6 @@ static void netfs_single_read_cache(struct netfs_io_reque= st *rreq, */ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) { - struct netfs_io_stream *stream =3D &rreq->io_streams[0]; struct netfs_io_subrequest *subreq; int ret =3D 0; =20 @@ -102,14 +101,7 @@ static int netfs_single_dispatch_read(struct netfs_io_= request *rreq) subreq->len =3D rreq->len; subreq->io_iter =3D rreq->buffer.iter; =20 - __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); - - spin_lock(&rreq->lock); - list_add_tail(&subreq->rreq_link, &stream->subrequests); - trace_netfs_sreq(subreq, netfs_sreq_trace_added); - /* Store list pointers before active flag */ - smp_store_release(&stream->active, true); - spin_unlock(&rreq->lock); + netfs_queue_read(rreq, subreq); =20 netfs_single_cache_prepare_read(rreq, subreq); switch (subreq->source) { @@ -121,10 +113,14 @@ static int netfs_single_dispatch_read(struct netfs_io= _request *rreq) goto cancel; } =20 + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); rreq->netfs_ops->issue_read(subreq); rreq->submitted +=3D subreq->len; break; case NETFS_READ_FROM_CACHE: + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); trace_netfs_sreq(subreq, netfs_sreq_trace_submit); netfs_single_read_cache(rreq, subreq); rreq->submitted +=3D subreq->len; @@ -134,14 +130,15 @@ static int netfs_single_dispatch_read(struct netfs_io= _request *rreq) pr_warn("Unexpected single-read source %u\n", subreq->source); WARN_ON_ONCE(true); ret =3D -EIO; - break; + goto cancel; } =20 - smp_wmb(); /* Write lists before ALL_QUEUED. */ - set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); return ret; cancel: - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + netfs_wake_collector(rreq); return ret; } From nobody Thu May 7 05:50:19 2026 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 EAE9746AF32 for ; Tue, 28 Apr 2026 13:19:09 +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=1777382351; cv=none; b=F8GIvYXB3Zb4ezL8M6AmThefwm6+mhFrnrPGsRkUDgeEyuhjgX3f9YFn2Un2bElbiV8z2/ZF14OdR2DE0thTJfr0eUI2fwABDjpFmcArB8RRM4yCgNQ/HGkjD6ELoPSMZBt68Fz+Zbaif3EGtW6Nu83VNekxEqeZzbNV6i3rsHs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382351; c=relaxed/simple; bh=xKMwK6XyTR2S1NYi1ll51KBolxqfDxdt7e/1pyWESn8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=T+6cUGZaF13nI4oXpxCz9A1xy0tJ0M8Ohk4EVJNkU1nxztLN3qd7fJhIRaUd/9RSfYuWd2wZ8oJiGOlfemM+i9pDhVCI9wnmQotiMEzyijbI/EczjJLqS/1iGRLPwKrnm+94YUf5m/zV79vd4WsY9iXNvVrvR4HO7Agsaj/Q4ZA= 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=Rw8QePfe; 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="Rw8QePfe" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382349; 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=I9C1Tq0bWPjxQKHy18dUxRo+Gdg144k5WV4DnkH5buY=; b=Rw8QePfesTfhsWQuEXy7zoEb5MpJcUPzdszfpHIEafTfI4td4NwU6wK2hRNAtKOuvq4F/E 8Aayb9IYwKjkVjic2RC8n3JvRRKXDO009RsOQ8SVzOWdqvje10cZmDyZw6BfLVBVt4ylwz 1Q78mHCFaPpqBIaZ4UpM3VXhVLW4ds0= 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-341-53Ejn19wPNSPVfWSlsUvmQ-1; Tue, 28 Apr 2026 09:19:03 -0400 X-MC-Unique: 53Ejn19wPNSPVfWSlsUvmQ-1 X-Mimecast-MFC-AGG-ID: 53Ejn19wPNSPVfWSlsUvmQ_1777382341 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 AC63E18B7CE3; Tue, 28 Apr 2026 13:18:12 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6E3771800348; Tue, 28 Apr 2026 13:18:08 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 02/24] netfs: Fix missing locking around retry adding new subreqs Date: Tue, 28 Apr 2026 14:17:32 +0100 Message-ID: <20260428131756.922303-3-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Fix netfs_retry_read_subrequests() and netfs_retry_write_stream() to take the appropriate lock when adding extra subrequests into stream->subrequests. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use o= ne work item") Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/read_retry.c | 6 +++++- fs/netfs/write_retry.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index cca9ac43c077..5ec548b996d6 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -175,7 +175,9 @@ static void netfs_retry_read_subrequests(struct netfs_i= o_request *rreq) list_for_each_entry_safe_from(subreq, tmp, &stream->subrequests, rreq_link) { trace_netfs_sreq(subreq, netfs_sreq_trace_superfluous); + spin_lock(&rreq->lock); list_del(&subreq->rreq_link); + spin_unlock(&rreq->lock); netfs_put_subrequest(subreq, netfs_sreq_trace_put_done); if (subreq =3D=3D to) break; @@ -203,8 +205,10 @@ static void netfs_retry_read_subrequests(struct netfs_= io_request *rreq) refcount_read(&subreq->ref), netfs_sreq_trace_new); =20 + spin_lock(&rreq->lock); list_add(&subreq->rreq_link, &to->rreq_link); - to =3D list_next_entry(to, rreq_link); + spin_unlock(&rreq->lock); + to =3D subreq; trace_netfs_sreq(subreq, netfs_sreq_trace_retry); =20 stream->sreq_max_len =3D umin(len, rreq->rsize); diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c index 29489a23a220..32735abfa03f 100644 --- a/fs/netfs/write_retry.c +++ b/fs/netfs/write_retry.c @@ -130,7 +130,9 @@ static void netfs_retry_write_stream(struct netfs_io_re= quest *wreq, list_for_each_entry_safe_from(subreq, tmp, &stream->subrequests, rreq_link) { trace_netfs_sreq(subreq, netfs_sreq_trace_discard); + spin_lock(&wreq->lock); list_del(&subreq->rreq_link); + spin_unlock(&wreq->lock); netfs_put_subrequest(subreq, netfs_sreq_trace_put_done); if (subreq =3D=3D to) break; @@ -153,8 +155,10 @@ static void netfs_retry_write_stream(struct netfs_io_r= equest *wreq, netfs_sreq_trace_new); trace_netfs_sreq(subreq, netfs_sreq_trace_split); =20 + spin_lock(&wreq->lock); list_add(&subreq->rreq_link, &to->rreq_link); - to =3D list_next_entry(to, rreq_link); + spin_unlock(&wreq->lock); + to =3D subreq; trace_netfs_sreq(subreq, netfs_sreq_trace_retry); =20 stream->sreq_max_len =3D len; From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 A7A6A47279C for ; Tue, 28 Apr 2026 13:19:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382351; cv=none; b=CWteT6rBe3okmWsrFtUDn8BHIYcqQFSzH+/EvZcnSbLTwLmQvYheBFg4rxxK07kHWj5WD08eIU30jNco3TSfA0/3vXcwvXRVFSHCP//tUcMnfI4A6+Y/nXQX5t0vpW3RzzBeWegHbnGBc9/W2gaQ0UCoS6De4xaXeNhKQaihkC8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382351; c=relaxed/simple; bh=eC23EDiq5qIGKq4ga5PcDMq7oBfvyAuTD3k4pCVIMa8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=L2nLu+Im+9xFZNVolVy+Fy5U6hensM+aWpdRp+bRN402hrfJwYiWcdUQqkUIwjfhBu3O3HjVZJXTebNUqkl/tn6kmvVCo9VGMl1mCaWoRzBpbccwCLDzsPs3ftlzMNBuwcQqLtybPIdpqp7e8uEU701eqH1cQlhL8nEM4tgcpas= 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=YzVF/04o; arc=none smtp.client-ip=170.10.133.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="YzVF/04o" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382348; 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=i68buiFPTMRi+TuWqayB/dF5laOKYLtNXEY9ZJMxAJg=; b=YzVF/04oxR0+M7QyMEZCsDYKsUeyhcKmkoxrHZ+LxXGhQzDUOZPhRmtKEpNUBTqKcyvxtH S6OornuP5TylfCjP01dQkW3k/NG1MSd+lazAdvSKsKe5MIgTy+19xRpBUtXc/8K+eoo+w7 Lo3G+xfZePkfg2XSrKbeVVZFG5KMjKs= 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-138-S5GolzNdN86Oer4dIiRNcw-1; Tue, 28 Apr 2026 09:19:05 -0400 X-MC-Unique: S5GolzNdN86Oer4dIiRNcw-1 X-Mimecast-MFC-AGG-ID: S5GolzNdN86Oer4dIiRNcw_1777382344 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 3732C1805A01; Tue, 28 Apr 2026 13:18:17 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4B33C196B8FC; Tue, 28 Apr 2026 13:18:14 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 03/24] netfs: Fix missing barriers when accessing stream->subrequests locklessly Date: Tue, 28 Apr 2026 14:17:33 +0100 Message-ID: <20260428131756.922303-4-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" The list of subrequests attached to stream->subrequests is accessed without locks by netfs_collect_read_results() and netfs_collect_write_results(), and then they access subreq->flags without taking a barrier after getting the subreq pointer from the list. Relatedly, the functions that build the list don't use any sort of write barrier when constructing the list to make sure that the NETFS_SREQ_IN_PROGRESS flag is perceived to be set first if no lock is taken. Fix this by: (1) Add a new list_add_tail_release() function that uses a release barrier to set the pointer to the new member of the list. (2) Add a new list_first_entry_or_null_acquire() function that uses an acquire barrier to read the pointer to the first member in a list (or return NULL). (3) Use list_add_tail_release() when adding a subreq to ->subrequests. (4) Use list_first_entry_or_null_acquire() when initially accessing the front of the list (when an item is removed, the pointer to the new front iterm is obtained under the same lock). Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use o= ne work item") Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Link: https://sashiko.dev/#/patchset/20260326104544.509518-1-dhowells%40red= hat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 3 ++- fs/netfs/misc.c | 1 + fs/netfs/read_collect.c | 6 ++++-- fs/netfs/write_collect.c | 6 ++++-- fs/netfs/write_issue.c | 3 ++- include/linux/list.h | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 6 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 286c93fc681e..7ec11893552d 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -168,7 +168,8 @@ void netfs_queue_read(struct netfs_io_request *rreq, * remove entries off of the front. */ spin_lock(&rreq->lock); - list_add_tail(&subreq->rreq_link, &stream->subrequests); + /* Write IN_PROGRESS before pointer to new subreq */ + list_add_tail_release(&subreq->rreq_link, &stream->subrequests); if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { if (!stream->active) { stream->collected_to =3D subreq->start; diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 6df89c92b10b..21357907b7ee 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -356,6 +356,7 @@ void netfs_wait_for_in_progress_stream(struct netfs_io_= request *rreq, DEFINE_WAIT(myself); =20 list_for_each_entry(subreq, &stream->subrequests, rreq_link) { + smp_rmb(); /* Read ->next before IN_PROGRESS. */ if (!netfs_check_subreq_in_progress(subreq)) continue; =20 diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index d2d902f46627..3c9b847885c2 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -205,8 +205,10 @@ static void netfs_collect_read_results(struct netfs_io= _request *rreq) * in progress. The issuer thread may be adding stuff to the tail * whilst we're doing this. */ - front =3D list_first_entry_or_null(&stream->subrequests, - struct netfs_io_subrequest, rreq_link); + front =3D list_first_entry_or_null_acquire(&stream->subrequests, + struct netfs_io_subrequest, rreq_link); + /* Read first subreq pointer before IN_PROGRESS flag. */ + while (front) { size_t transferred; =20 diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c index b194447f4b11..7fbf50907a7f 100644 --- a/fs/netfs/write_collect.c +++ b/fs/netfs/write_collect.c @@ -228,8 +228,10 @@ static void netfs_collect_write_results(struct netfs_i= o_request *wreq) if (!smp_load_acquire(&stream->active)) continue; =20 - front =3D list_first_entry_or_null(&stream->subrequests, - struct netfs_io_subrequest, rreq_link); + front =3D list_first_entry_or_null_acquire(&stream->subrequests, + struct netfs_io_subrequest, rreq_link); + /* Read first subreq pointer before IN_PROGRESS flag. */ + while (front) { trace_netfs_collect_sreq(wreq, front); //_debug("sreq [%x] %llx %zx/%zx", diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 2db688f94125..b0e9690bb90c 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -204,7 +204,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq, * remove entries off of the front. */ spin_lock(&wreq->lock); - list_add_tail(&subreq->rreq_link, &stream->subrequests); + /* Write IN_PROGRESS before pointer to new subreq */ + list_add_tail_release(&subreq->rreq_link, &stream->subrequests); if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { if (!stream->active) { stream->collected_to =3D subreq->start; diff --git a/include/linux/list.h b/include/linux/list.h index 00ea8e5fb88b..09d979976b3b 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -191,6 +191,29 @@ static inline void list_add_tail(struct list_head *new= , struct list_head *head) __list_add(new, head->prev, head); } =20 +/** + * list_add_tail_release - add a new entry with release barrier + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head, using a release barrier t= o set + * the ->next pointer that points to it. This is useful for implementing + * queues, in particular one that the elements will be walked through forw= ards + * locklessly. + */ +static inline void list_add_tail_release(struct list_head *new, + struct list_head *head) +{ + struct list_head *prev =3D head->prev; + + if (__list_add_valid(new, prev, head)) { + new->next =3D head; + new->prev =3D prev; + head->prev =3D new; + smp_store_release(&prev->next, new); + } +} + /* * Delete a list entry by making the prev/next entries * point to each other. @@ -644,6 +667,20 @@ static inline void list_splice_tail_init(struct list_h= ead *list, pos__ !=3D head__ ? list_entry(pos__, type, member) : NULL; \ }) =20 +/** + * list_first_entry_or_null_acquire - get the first element from a list wi= th barrier + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null_acquire(ptr, type, member) ({ \ + struct list_head *head__ =3D (ptr); \ + struct list_head *pos__ =3D smp_load_acquire(&head__->next); \ + pos__ !=3D head__ ? list_entry(pos__, type, member) : NULL; \ +}) + /** * list_last_entry_or_null - get the last element from a list * @ptr: the list head to take the element from. From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 0C1D8472766 for ; Tue, 28 Apr 2026 13:19:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382348; cv=none; b=FThiyg3RgpzqAWJPGZGbAPGM8BMl6C+bqz8esoEWSdWKFjTVZNV9Dwqq0FyJb1Q0Y5c6VEyJnZ1PtU+ZM0rmJVcE68rHgG0fE0xtEvyZo3Lui95sFc6gz7lOnSvZVyMaB6hV9507fC5CGLPmqLAo1GULQtNgWt5RQYhSJz0z2Z0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382348; c=relaxed/simple; bh=xt/VtEfH0gIsDmidCvLtu7xSES7YK7i6BHGIB3e+zRY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M92bA1utf+jEIf8rFA8ed6C9ayxgGZolZ8xdTzmJa3Ngl9i06vYkW1N+nDD+wd7KUmKPHffsWjL73mnSp/JFbCiQZexE7g6eIiYVpZgCKCzsA64LNNxBnpRQYr4QP5rqRuSorB05rLygoaZCaQz9af6O8ah/ErnNd+/cHzS2BJU= 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=dyjptN1h; arc=none smtp.client-ip=170.10.133.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="dyjptN1h" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382346; 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=NNTWy1b19ZoNmt6S5T/uvk5jPsS1NSlNmD1y6ywnbBw=; b=dyjptN1hUkxUDEm0uDpiKiGfyHAZ9YPJQ0VI1VfhcXI6VrQtPksnkgGlP+s7D+WJdky+Jb tzVPw+mzA9myClyxyjIK8pndujX81kSNQ/FTaT0u0wOfCmMSrp8yRtvwNPQgqmemGCOtez RE4bYW90WF5UKb3g+CDiIL2tH0czBSA= 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-132-7zCkveG9NRmxk-gbMKgZjA-1; Tue, 28 Apr 2026 09:19:02 -0400 X-MC-Unique: 7zCkveG9NRmxk-gbMKgZjA-1 X-Mimecast-MFC-AGG-ID: 7zCkveG9NRmxk-gbMKgZjA_1777382338 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 CAA8B186B1A4; Tue, 28 Apr 2026 13:18:21 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1A22719560AB; Tue, 28 Apr 2026 13:18:18 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 04/24] netfs: Fix netfs_read_to_pagecache() to pause on subreq failure Date: Tue, 28 Apr 2026 14:17:34 +0100 Message-ID: <20260428131756.922303-5-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" Fix netfs_read_to_pagecache() so that it pauses the generation of new subrequests if an already-issued subrequest fails. Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 7ec11893552d..228d058bf1a2 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -299,6 +299,11 @@ static void netfs_read_to_pagecache(struct netfs_io_re= quest *rreq, } =20 netfs_issue_read(rreq, subreq); + + if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags)) + netfs_wait_for_paused_read(rreq); + if (test_bit(NETFS_RREQ_FAILED, &rreq->flags)) + break; cond_resched(); } while (size > 0); From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 837CE451076 for ; Tue, 28 Apr 2026 13:18:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382334; cv=none; b=LkuPsZ3WP8FsLjzOiNaMx7Hjot/IjTrWIajSDbnryf3GjpEw3d+6U6I8IpBuqjOBXOt0hq2ziAm+GAoozT50vUEBBamuEhlBI/LarboYCLeW0vBXXYdqwSaPwpP6yrPcfzuKyQyizvbu5e7RO2P/dlaOKlANAYuYTHdOmXN85/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382334; c=relaxed/simple; bh=ponMVlgqXWu1xMcVG+oIYftxD7t5QVfbT0bpsJl4bPU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RegHo4L/cuWImvCzrZNF05Y8nySXyHIn8epvlZ59NZvY3hiubqZUpy/6TNQWb2Dkb1MTTb8hMihKLMnYsJh4BnvIGzoyP2UOLIEry2CYv2FJ0bOxqoWKCsZdP9spY/vIGgTIZEu7Gd31SY7ItnRgZ5S9zBKUP/ztbh9IJrD+8C8= 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=GudFAaeU; arc=none smtp.client-ip=170.10.133.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="GudFAaeU" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382330; 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=BNSFZ4m+qr+OyqaFsDNeNXKvjaAQNIFnb/KirzsJEJ0=; b=GudFAaeUpZQXxNnwHjmHvb6LciVGF7/BGtYVodOuSQ8yLPRWmysQXKz3buQA9iNqTJVVvN pzvV8N8lG5Q8Z5ScI1HqVGOoTcWFzKCWaPHcSpsepR3jT/ruWQNW48Ebm3Z0Lf3qh0BSdf qxh6BvUXMkEWFP+cJlCCMYW43uYwr9s= 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-610-k6KSrxW1M7C7qQtbMyHWYw-1; Tue, 28 Apr 2026 09:18:46 -0400 X-MC-Unique: k6KSrxW1M7C7qQtbMyHWYw-1 X-Mimecast-MFC-AGG-ID: k6KSrxW1M7C7qQtbMyHWYw_1777382324 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 DA68E19772F8; Tue, 28 Apr 2026 13:18:27 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7DA4919560AB; Tue, 28 Apr 2026 13:18:23 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 05/24] netfs: Fix potential for tearing in ->remote_i_size and ->zero_point Date: Tue, 28 Apr 2026 14:17:35 +0100 Message-ID: <20260428131756.922303-6-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" Fix potential tearing in using ->remote_i_size and ->zero_point by copying i_size_read() and i_size_write() and using the same seqcount as for i_size. Fixes: 4058f742105e ("netfs: Keep track of the actual remote file size") Fixes: 100ccd18bb41 ("netfs: Optimise away reads above the point at which t= here can be no data") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/9p/vfs_inode.c | 2 +- fs/9p/vfs_inode_dotl.c | 4 +- fs/afs/inode.c | 8 +- fs/afs/write.c | 2 +- fs/netfs/buffered_read.c | 5 +- fs/netfs/buffered_write.c | 2 +- fs/netfs/direct_write.c | 4 +- fs/netfs/misc.c | 13 +- fs/netfs/write_collect.c | 3 +- fs/smb/client/cifsfs.c | 28 +-- fs/smb/client/cifssmb.c | 2 +- fs/smb/client/file.c | 9 +- fs/smb/client/inode.c | 9 +- fs/smb/client/readdir.c | 3 +- fs/smb/client/smb2ops.c | 16 +- fs/smb/client/smb2pdu.c | 2 +- include/linux/netfs.h | 362 +++++++++++++++++++++++++++++++++++++- 17 files changed, 413 insertions(+), 61 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index d1508b1fe109..b13156ac2f1f 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1141,7 +1141,7 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *= inode, mode |=3D inode->i_mode & ~S_IALLUGO; inode->i_mode =3D mode; =20 - v9inode->netfs.remote_i_size =3D stat->length; + netfs_write_remote_i_size(&v9inode->netfs, stat->length); if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) v9fs_i_size_write(inode, stat->length); /* not real number of blocks, but 512 byte ones ... */ diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 71796a89bcf4..81d6150a8ae4 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -634,7 +634,7 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct = inode *inode, mode |=3D inode->i_mode & ~S_IALLUGO; inode->i_mode =3D mode; =20 - v9inode->netfs.remote_i_size =3D stat->st_size; + netfs_write_remote_i_size(&v9inode->netfs, stat->st_size); if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) v9fs_i_size_write(inode, stat->st_size); inode->i_blocks =3D stat->st_blocks; @@ -664,7 +664,7 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct = inode *inode, } if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) && stat->st_result_mask & P9_STATS_SIZE) { - v9inode->netfs.remote_i_size =3D stat->st_size; + netfs_write_remote_i_size(&v9inode->netfs, stat->st_size); v9fs_i_size_write(inode, stat->st_size); } if (stat->st_result_mask & P9_STATS_BLOCKS) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index a5173434f786..06e25e1b12df 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -343,11 +343,11 @@ static void afs_apply_status(struct afs_operation *op, * idea of what the size should be that's not the same as * what's on the server. */ - vnode->netfs.remote_i_size =3D status->size; + netfs_write_remote_i_size(&vnode->netfs, status->size); if (change_size || status->size > i_size_read(inode)) { afs_set_i_size(vnode, status->size); if (unexpected_jump) - vnode->netfs.zero_point =3D status->size; + netfs_write_zero_point(&vnode->netfs, status->size); inode_set_ctime_to_ts(inode, t); inode_set_atime_to_ts(inode, t); } @@ -709,7 +709,7 @@ int afs_getattr(struct mnt_idmap *idmap, const struct p= ath *path, * it, but we need to give userspace the server's size. */ if (S_ISDIR(inode->i_mode)) - stat->size =3D vnode->netfs.remote_i_size; + stat->size =3D netfs_read_remote_i_size(&vnode->netfs); } while (read_seqretry(&vnode->cb_lock, seq)); =20 return 0; @@ -889,7 +889,7 @@ int afs_setattr(struct mnt_idmap *idmap, struct dentry = *dentry, */ if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) && attr->ia_size < i_size && - attr->ia_size > vnode->netfs.remote_i_size) { + attr->ia_size > netfs_read_remote_i_size(&vnode->netfs)) { truncate_setsize(inode, attr->ia_size); netfs_resize_file(&vnode->netfs, size, false); fscache_resize_cookie(afs_vnode_cache(vnode), diff --git a/fs/afs/write.c b/fs/afs/write.c index fcfed9d24e0a..c087151c4bf9 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -142,7 +142,7 @@ static void afs_issue_write_worker(struct work_struct *= work) afs_begin_vnode_operation(op); =20 op->store.write_iter =3D &subreq->io_iter; - op->store.i_size =3D umax(pos + len, vnode->netfs.remote_i_size); + op->store.i_size =3D umax(pos + len, netfs_read_remote_i_size(&vnode->net= fs)); op->mtime =3D inode_get_mtime(&vnode->netfs.inode); =20 afs_wait_for_operation(op); diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 228d058bf1a2..c0fe6dc40d87 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -233,7 +233,8 @@ static void netfs_read_to_pagecache(struct netfs_io_req= uest *rreq, source =3D netfs_cache_prepare_read(rreq, subreq, rreq->i_size); subreq->source =3D source; if (source =3D=3D NETFS_DOWNLOAD_FROM_SERVER) { - unsigned long long zp =3D umin(ictx->zero_point, rreq->i_size); + unsigned long long zero_point =3D netfs_read_zero_point(ictx); + unsigned long long zp =3D umin(zero_point, rreq->i_size); size_t len =3D subreq->len; =20 if (unlikely(rreq->origin =3D=3D NETFS_READ_SINGLE)) @@ -249,7 +250,7 @@ static void netfs_read_to_pagecache(struct netfs_io_req= uest *rreq, pr_err("ZERO-LEN READ: R=3D%08x[%x] l=3D%zx/%zx s=3D%llx z=3D%llx i=3D= %llx", rreq->debug_id, subreq->debug_index, subreq->len, size, - subreq->start, ictx->zero_point, rreq->i_size); + subreq->start, zero_point, rreq->i_size); netfs_cancel_read(subreq, ret); break; } diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 05ea5b0cc0e8..fc94eb1ef27b 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -230,7 +230,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, * server would just return a block of zeros or a short read if * we try to read it. */ - if (fpos >=3D ctx->zero_point) { + if (fpos >=3D netfs_read_zero_point(ctx)) { folio_zero_segment(folio, 0, offset); copied =3D copy_folio_from_iter_atomic(folio, offset, part, iter); if (unlikely(copied =3D=3D 0)) diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c index f9ab69de3e29..96c1dad04168 100644 --- a/fs/netfs/direct_write.c +++ b/fs/netfs/direct_write.c @@ -376,8 +376,8 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb,= struct iov_iter *from) if (ret < 0) goto out; end =3D iocb->ki_pos + iov_iter_count(from); - if (end > ictx->zero_point) - ictx->zero_point =3D end; + if (end > netfs_read_zero_point(ictx)) + netfs_write_zero_point(ictx, end); =20 fscache_invalidate(netfs_i_cookie(ictx), NULL, i_size_read(inode), FSCACHE_INVAL_DIO_WRITE); diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 21357907b7ee..67e089fa0b0b 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -221,8 +221,8 @@ void netfs_invalidate_folio(struct folio *folio, size_t= offset, size_t length) unsigned long long fpos =3D folio_pos(folio), end; =20 end =3D umin(fpos + flen, i_size); - if (fpos < i_size && end > ctx->zero_point) - ctx->zero_point =3D end; + if (fpos < i_size && end > netfs_read_zero_point(ctx)) + netfs_write_zero_point(ctx, end); } =20 folio_wait_private_2(folio); /* [DEPRECATED] */ @@ -293,14 +293,15 @@ EXPORT_SYMBOL(netfs_invalidate_folio); bool netfs_release_folio(struct folio *folio, gfp_t gfp) { struct netfs_inode *ctx =3D netfs_inode(folio_inode(folio)); - unsigned long long end; + unsigned long long i_size, remote_i_size, zero_point, end; =20 if (folio_test_dirty(folio)) return false; =20 - end =3D umin(folio_next_pos(folio), i_size_read(&ctx->inode)); - if (end > ctx->zero_point) - ctx->zero_point =3D end; + netfs_read_sizes(ctx, &i_size, &remote_i_size, &zero_point); + end =3D umin(folio_next_pos(folio), i_size); + if (end > zero_point) + netfs_write_zero_point(ctx, end); =20 if (folio_test_private(folio)) return false; diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c index 7fbf50907a7f..a630403fe75e 100644 --- a/fs/netfs/write_collect.c +++ b/fs/netfs/write_collect.c @@ -69,8 +69,7 @@ int netfs_folio_written_back(struct folio *folio) unsigned long long fend; =20 fend =3D folio_pos(folio) + finfo->dirty_offset + finfo->dirty_len; - if (fend > ictx->zero_point) - ictx->zero_point =3D fend; + netfs_push_back_zero_point(ictx, fend); =20 folio_detach_private(folio); group =3D finfo->netfs_group; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 9f76b0347fa9..dacf35676e75 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -434,7 +434,8 @@ cifs_alloc_inode(struct super_block *sb) spin_lock_init(&cifs_inode->writers_lock); cifs_inode->writers =3D 0; cifs_inode->netfs.inode.i_blkbits =3D 14; /* 2**14 =3D CIFS_MAX_MSGSIZE = */ - cifs_inode->netfs.remote_i_size =3D 0; + cifs_inode->netfs._remote_i_size =3D 0; + cifs_inode->netfs._zero_point =3D 0; cifs_inode->uniqueid =3D 0; cifs_inode->createtime =3D 0; cifs_inode->epoch =3D 0; @@ -1303,7 +1304,8 @@ static loff_t cifs_remap_file_range(struct file *src_= file, loff_t off, struct cifsFileInfo *smb_file_src =3D src_file->private_data; struct cifsFileInfo *smb_file_target =3D dst_file->private_data; struct cifs_tcon *target_tcon, *src_tcon; - unsigned long long destend, fstart, fend, old_size, new_size; + unsigned long long i_size, old_size, new_size, zero_point; + unsigned long long destend, fstart, fend; unsigned int xid; int rc; =20 @@ -1347,7 +1349,7 @@ static loff_t cifs_remap_file_range(struct file *src_= file, loff_t off, * Advance the EOF marker after the flush above to the end of the range * if it's short of that. */ - if (src_cifsi->netfs.remote_i_size < off + len) { + if (netfs_read_remote_i_size(&src_cifsi->netfs) < off + len) { rc =3D cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + l= en); if (rc < 0) goto unlock; @@ -1368,16 +1370,16 @@ static loff_t cifs_remap_file_range(struct file *sr= c_file, loff_t off, rc =3D cifs_flush_folio(target_inode, destend, &fstart, &fend, false); if (rc) goto unlock; - if (fend > target_cifsi->netfs.zero_point) - target_cifsi->netfs.zero_point =3D fend + 1; - old_size =3D target_cifsi->netfs.remote_i_size; + + netfs_read_sizes(&target_cifsi->netfs, &i_size, &old_size, &zero_point); + if (fend > zero_point) + netfs_write_zero_point(&target_cifsi->netfs, fend + 1); =20 /* Discard all the folios that overlap the destination region. */ cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend); truncate_inode_pages_range(&target_inode->i_data, fstart, fend); =20 - fscache_invalidate(cifs_inode_cookie(target_inode), NULL, - i_size_read(target_inode), 0); + fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0); =20 rc =3D -EOPNOTSUPP; if (target_tcon->ses->server->ops->duplicate_extents) { @@ -1402,8 +1404,8 @@ static loff_t cifs_remap_file_range(struct file *src_= file, loff_t off, rc =3D -EINVAL; } } - if (rc =3D=3D 0 && new_size > target_cifsi->netfs.zero_point) - target_cifsi->netfs.zero_point =3D new_size; + if (rc =3D=3D 0) + netfs_push_back_zero_point(&target_cifsi->netfs, new_size); } =20 /* force revalidate of size and timestamps of target file now @@ -1474,7 +1476,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, * Advance the EOF marker after the flush above to the end of the range * if it's short of that. */ - if (src_cifsi->netfs.remote_i_size < off + len) { + if (netfs_read_remote_i_size(&src_cifsi->netfs) < off + len) { rc =3D cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + l= en); if (rc < 0) goto unlock; @@ -1502,8 +1504,8 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, fscache_resize_cookie(cifs_inode_cookie(target_inode), i_size_read(target_inode)); } - if (rc > 0 && destoff + rc > target_cifsi->netfs.zero_point) - target_cifsi->netfs.zero_point =3D destoff + rc; + if (rc > 0) + netfs_push_back_zero_point(&target_cifsi->netfs, destoff + rc); } =20 file_accessed(src_file); diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 3990a9012264..102dd9dde760 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -1538,7 +1538,7 @@ cifs_readv_callback(struct TCP_Server_Info *server, s= truct mid_q_entry *mid) } else { size_t trans =3D rdata->subreq.transferred + rdata->got_bytes; if (trans < rdata->subreq.len && - rdata->subreq.start + trans >=3D ictx->remote_i_size) { + rdata->subreq.start + trans >=3D netfs_read_remote_i_size(ictx)) { rdata->result =3D 0; __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); } else if (rdata->got_bytes > 0) { diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 664a2c223089..c1b152f8d20f 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -2518,16 +2518,19 @@ void cifs_write_subrequest_terminated(struct cifs_i= o_subrequest *wdata, ssize_t { struct netfs_io_request *wreq =3D wdata->rreq; struct netfs_inode *ictx =3D netfs_inode(wreq->inode); + unsigned long long i_size, remote_i_size, zero_point; loff_t wrend; =20 if (result > 0) { + netfs_read_sizes(ictx, &i_size, &remote_i_size, &zero_point); + wrend =3D wdata->subreq.start + wdata->subreq.transferred + result; =20 - if (wrend > ictx->zero_point && + if (wrend > zero_point && (wdata->rreq->origin =3D=3D NETFS_UNBUFFERED_WRITE || wdata->rreq->origin =3D=3D NETFS_DIO_WRITE)) - ictx->zero_point =3D wrend; - if (wrend > ictx->remote_i_size) + netfs_write_zero_point(ictx, wrend); + if (wrend > remote_i_size) netfs_resize_file(ictx, wrend, true); } =20 diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 16a5310155d5..620b9f4df65b 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -119,7 +119,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_= fattr *fattr) fattr->cf_mtime =3D timestamp_truncate(fattr->cf_mtime, inode); mtime =3D inode_get_mtime(inode); if (timespec64_equal(&mtime, &fattr->cf_mtime) && - cifs_i->netfs.remote_i_size =3D=3D fattr->cf_eof) { + netfs_read_remote_i_size(&cifs_i->netfs) =3D=3D fattr->cf_eof) { cifs_dbg(FYI, "%s: inode %llu is unchanged\n", __func__, cifs_i->uniqueid); return; @@ -174,7 +174,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fa= ttr *fattr, return -ESTALE; } if (inode_state_read_once(inode) & I_NEW) - CIFS_I(inode)->netfs.zero_point =3D fattr->cf_eof; + netfs_write_zero_point(&CIFS_I(inode)->netfs, fattr->cf_eof); =20 cifs_revalidate_cache(inode, fattr); =20 @@ -212,7 +212,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fa= ttr *fattr, else clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); =20 - cifs_i->netfs.remote_i_size =3D fattr->cf_eof; + netfs_write_remote_i_size_locked(&cifs_i->netfs, fattr->cf_eof); /* * Can't safely change the file size here if the client is writing to * it due to potential races. @@ -2772,7 +2772,8 @@ cifs_revalidate_mapping(struct inode *inode) if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_RW_CACHE) goto skip_invalidate; =20 - cifs_inode->netfs.zero_point =3D cifs_inode->netfs.remote_i_size; + netfs_write_zero_point(&cifs_inode->netfs, + netfs_read_remote_i_size(&cifs_inode->netfs)); rc =3D filemap_invalidate_inode(inode, true, 0, LLONG_MAX); if (rc) { cifs_dbg(VFS, "%s: invalidate inode %p failed with rc %d\n", diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index be22bbc4a65a..d88682e89ec0 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -143,7 +143,8 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *n= ame, fattr->cf_rdev =3D inode->i_rdev; fattr->cf_uid =3D inode->i_uid; fattr->cf_gid =3D inode->i_gid; - fattr->cf_eof =3D CIFS_I(inode)->netfs.remote_i_size; + fattr->cf_eof =3D + netfs_read_remote_i_size(&CIFS_I(inode)->netfs); fattr->cf_symlink_target =3D NULL; } else { CIFS_I(inode)->time =3D 0; diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 7f346ee50289..98638ac17b7b 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3404,7 +3404,7 @@ static long smb3_zero_range(struct file *file, struct= cifs_tcon *tcon, filemap_invalidate_lock(inode->i_mapping); =20 i_size =3D i_size_read(inode); - remote_size =3D ictx->remote_i_size; + remote_size =3D netfs_read_remote_i_size(ictx); if (offset + len >=3D remote_size && offset < i_size) { unsigned long long top =3D umin(offset + len, i_size); =20 @@ -3439,8 +3439,8 @@ static long smb3_zero_range(struct file *file, struct= cifs_tcon *tcon, if (rc >=3D 0) { truncate_setsize(inode, new_size); netfs_resize_file(&cifsi->netfs, new_size, true); - if (offset < cifsi->netfs.zero_point) - cifsi->netfs.zero_point =3D offset; + if (offset < netfs_read_zero_point(&cifsi->netfs)) + netfs_write_zero_point(&cifsi->netfs, offset); fscache_resize_cookie(cifs_inode_cookie(inode), new_size); } } @@ -3506,13 +3506,13 @@ static long smb3_punch_hole(struct file *file, stru= ct cifs_tcon *tcon, * EOF update will end up in the wrong place. */ i_size =3D i_size_read(inode); - remote_i_size =3D netfs_inode(inode)->remote_i_size; + remote_i_size =3D netfs_read_remote_i_size(netfs_inode(inode)); if (end > remote_i_size && i_size > remote_i_size) { unsigned long long extend_to =3D umin(end, i_size); rc =3D SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, extend_to); if (rc >=3D 0) - netfs_inode(inode)->remote_i_size =3D extend_to; + netfs_write_remote_i_size(netfs_inode(inode), extend_to); } =20 unlock: @@ -3794,7 +3794,7 @@ static long smb3_collapse_range(struct file *file, st= ruct cifs_tcon *tcon, goto out_2; =20 truncate_pagecache_range(inode, off, old_eof); - ictx->zero_point =3D old_eof; + netfs_write_zero_point(ictx, old_eof); netfs_wait_for_outstanding_io(inode); =20 rc =3D smb2_copychunk_range(xid, cfile, cfile, off + len, @@ -3812,7 +3812,7 @@ static long smb3_collapse_range(struct file *file, st= ruct cifs_tcon *tcon, =20 truncate_setsize(inode, new_eof); netfs_resize_file(&cifsi->netfs, new_eof, true); - ictx->zero_point =3D new_eof; + netfs_write_zero_point(ictx, new_eof); fscache_resize_cookie(cifs_inode_cookie(inode), new_eof); out_2: filemap_invalidate_unlock(inode->i_mapping); @@ -3861,7 +3861,7 @@ static long smb3_insert_range(struct file *file, stru= ct cifs_tcon *tcon, rc =3D smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); if (rc < 0) goto out_2; - cifsi->netfs.zero_point =3D new_eof; + netfs_write_zero_point(&cifsi->netfs, new_eof); =20 rc =3D smb3_zero_data(file, tcon, off, len, xid); if (rc < 0) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index cb61051f9af3..368472589fe6 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4708,7 +4708,7 @@ smb2_readv_callback(struct TCP_Server_Info *server, s= truct mid_q_entry *mid) } else { size_t trans =3D rdata->subreq.transferred + rdata->got_bytes; if (trans < rdata->subreq.len && - rdata->subreq.start + trans >=3D ictx->remote_i_size) { + rdata->subreq.start + trans >=3D netfs_read_remote_i_size(ictx)) { __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); rdata->result =3D 0; } diff --git a/include/linux/netfs.h b/include/linux/netfs.h index ba17ac5bf356..1b1db8e6cdf1 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -62,8 +62,8 @@ struct netfs_inode { struct fscache_cookie *cache; #endif struct mutex wb_lock; /* Writeback serialisation */ - loff_t remote_i_size; /* Size of the remote file */ - loff_t zero_point; /* Size after which we assume there's no data + loff_t _remote_i_size; /* Size of the remote file */ + loff_t _zero_point; /* Size after which we assume there's no data * on the server */ atomic_t io_count; /* Number of outstanding reqs */ unsigned long flags; @@ -474,6 +474,323 @@ static inline struct netfs_inode *netfs_inode(struct = inode *inode) return container_of(inode, struct netfs_inode, inode); } =20 +/** + * netfs_read_remote_i_size - Read remote_i_size safely + * @ictx: The inode context to access + * + * Read remote_i_size safely without the potential for tearing on 32-bit + * arches. + * + * NOTE: in a 32bit arch with a preemptable kernel and an UP compile the + * i_size_read/write must be atomic with respect to the local cpu (unlike = with + * preempt disabled), but they don't need to be atomic with respect to oth= er + * cpus like in true SMP (so they need either to either locally disable irq + * around the read or for example on x86 they can be still implemented as a + * cmpxchg8b without the need of the lock prefix). For SMP compiles and 6= 4bit + * archs it makes no difference if preempt is enabled or not. + */ +static inline unsigned long long netfs_read_remote_i_size(const struct net= fs_inode *ictx) +{ + unsigned long long remote_i_size; + +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + const struct inode *inode =3D &ictx->inode; + unsigned int seq; + + do { + seq =3D read_seqcount_begin(&inode->i_size_seqcount); + remote_i_size =3D ictx->_remote_i_size; + } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + remote_i_size =3D ictx->_remote_i_size; + preempt_enable(); +#else + /* Pairs with smp_store_release() in netfs_write_remote_i_size() */ + remote_i_size =3D smp_load_acquire(&ictx->_remote_i_size); +#endif + return remote_i_size; +} + +/* + * netfs_write_remote_i_size_locked - Set remote_i_size safely + * @ictx: The inode context to access + * @remote_i_size: The new value for the size of the file on the server + * + * Set remote_i_size safely without the potential for tearing on 32-bit ar= ches. + * The caller must hold inode->i_lock. + * + * NOTE: unlike netfs_read_remote_i_size(), netfs_write_remote_i_size() do= es + * need locking around it (normally i_rwsem), otherwise on 32bit/SMP an up= date + * of i_size_seqcount can be lost, resulting in subsequent i_size_read() c= alls + * spinning forever. + */ +static inline void netfs_write_remote_i_size_locked(struct netfs_inode *ic= tx, + unsigned long long remote_i_size) +{ +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + struct inode *inode =3D &ictx->inode; + + /* The caller must hold inode->i_lock. */ + write_seqcount_begin(&inode->i_size_seqcount); + ictx->_remote_i_size =3D remote_i_size; + write_seqcount_end(&inode->i_size_seqcount); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + ictx->_remote_i_size =3D remote_i_size; + preempt_enable(); +#else + /* + * Pairs with smp_load_acquire() in netfs_read_remote_i_size() to + * ensure changes related to inode size (such as page contents) are + * visible before we see the changed inode size. + */ + smp_store_release(&ictx->_remote_i_size, remote_i_size); +#endif +} + +/* + * netfs_write_remote_i_size - Set remote_i_size safely + * @ictx: The inode context to access + * @remote_i_size: The new value for the size of the file on the server + * + * Set remote_i_size safely without the potential for tearing on 32-bit ar= ches. + * + * NOTE: unlike netfs_read_remote_i_size(), netfs_write_remote_i_size() do= es + * need locking around it (normally i_rwsem), otherwise on 32bit/SMP an up= date + * of i_size_seqcount can be lost, resulting in subsequent i_size_read() c= alls + * spinning forever. + */ +static inline void netfs_write_remote_i_size(struct netfs_inode *ictx, + unsigned long long remote_i_size) +{ +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + struct inode *inode =3D &ictx->inode; + + spin_lock(&inode->i_lock); + write_seqcount_begin(&inode->i_size_seqcount); + ictx->_remote_i_size =3D remote_i_size; + write_seqcount_end(&inode->i_size_seqcount); + spin_unlock(&inode->i_lock); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + ictx->_remote_i_size =3D remote_i_size; + preempt_enable(); +#else + /* + * Pairs with smp_load_acquire() in netfs_read_remote_i_size() to + * ensure changes related to inode size (such as page contents) are + * visible before we see the changed inode size. + */ + smp_store_release(&ictx->_remote_i_size, remote_i_size); +#endif +} + +/** + * netfs_read_zero_point - Read zero_point safely + * @ictx: The inode context to access + * + * Read zero_point safely without the potential for tearing on 32-bit + * arches. + * + * NOTE: in a 32bit arch with a preemptable kernel and an UP compile the + * i_size_read/write must be atomic with respect to the local cpu (unlike = with + * preempt disabled), but they don't need to be atomic with respect to oth= er + * cpus like in true SMP (so they need either to either locally disable irq + * around the read or for example on x86 they can be still implemented as a + * cmpxchg8b without the need of the lock prefix). For SMP compiles and 6= 4bit + * archs it makes no difference if preempt is enabled or not. + */ +static inline unsigned long long netfs_read_zero_point(const struct netfs_= inode *ictx) +{ + unsigned long long zero_point; + +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + const struct inode *inode =3D &ictx->inode; + unsigned int seq; + + do { + seq =3D read_seqcount_begin(&inode->i_size_seqcount); + zero_point =3D ictx->_zero_point; + } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + zero_point =3D ictx->_zero_point; + preempt_enable(); +#else + /* Pairs with smp_store_release() in netfs_write_zero_point() */ + zero_point =3D smp_load_acquire(&ictx->_zero_point); +#endif + return zero_point; +} + +/* + * netfs_write_zero_point - Set zero_point safely + * @ictx: The inode context to access + * @zero_point: The new value for the point beyond which the server has no= data + * + * Set zero_point safely without the potential for tearing on 32-bit arche= s. + * + * NOTE: unlike netfs_read_zero_point(), netfs_write_zero_point() does need + * locking around it (normally i_rwsem), otherwise on 32bit/SMP an update = of + * i_size_seqcount can be lost, resulting in subsequent read calls spinning + * forever. + */ +static inline void netfs_write_zero_point(struct netfs_inode *ictx, + unsigned long long zero_point) +{ +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + struct inode *inode =3D &ictx->inode; + + spin_lock(&inode->i_lock); + write_seqcount_begin(&inode->i_size_seqcount); + ictx->_zero_point =3D zero_point; + write_seqcount_end(&inode->i_size_seqcount); + spin_unlock(&inode->i_lock); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + ictx->_zero_point =3D zero_point; + preempt_enable(); +#else + /* + * Pairs with smp_load_acquire() in netfs_read_zero_point() to + * ensure changes related to inode size (such as page contents) are + * visible before we see the changed inode size. + */ + smp_store_release(&ictx->_zero_point, zero_point); +#endif +} + +/** + * netfs_push_back_zero_point - Push back the zero point if unknown data n= ow beyond it + * ictx: The inode context to access + * to: The end of a new region of unknown data + * + * Move back the zero_point if we cause a region of unknown data to appear + * beyond it (such as doing a copy_file_range). + */ +static inline void netfs_push_back_zero_point(struct netfs_inode *ictx, + unsigned long long to) +{ +#if BITS_PER_LONG=3D=3D32 && (defined(CONFIG_SMP) || !defined(CONFIG_PREEM= PTION)) + struct inode *inode =3D &ictx->inode; + + spin_lock(&inode->i_lock); + write_seqcount_begin(&inode->i_size_seqcount); + if (to > ictx->_zero_point) + ictx->_zero_point =3D to; + write_seqcount_end(&inode->i_size_seqcount); + spin_unlock(&inode->i_lock); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + if (to > ictx->_zero_point) + ictx->_zero_point =3D to; + preempt_enable(); +#else + unsigned long long cur =3D READ_ONCE(ictx->_zero_point), old; + + while (to > cur) { + old =3D cmpxchg_release(&ictx->_zero_point, cur, to); + if (old =3D=3D cur) + break; + cur =3D old; + } +#endif +} + +/** + * netfs_read_sizes - Read remote_i_size and zero_point safely + * @ictx: The inode context to access + * @i_size: Where to return the local file size. + * @remote_i_size: Where to return the size of the file on the server + * @zero_point: Where to return the the point beyond which the server has = no data + * + * Read remote_i_size and zero_point safely without the potential for tear= ing + * on 32-bit arches. + * + * NOTE: in a 32bit arch with a preemptable kernel and an UP compile the + * i_size_read/write must be atomic with respect to the local cpu (unlike = with + * preempt disabled), but they don't need to be atomic with respect to oth= er + * cpus like in true SMP (so they need either to either locally disable irq + * around the read or for example on x86 they can be still implemented as a + * cmpxchg8b without the need of the lock prefix). For SMP compiles and 6= 4bit + * archs it makes no difference if preempt is enabled or not. + */ +static inline void netfs_read_sizes(const struct netfs_inode *ictx, + unsigned long long *i_size, + unsigned long long *remote_i_size, + unsigned long long *zero_point) +{ + const struct inode *inode =3D &ictx->inode; +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + unsigned int seq; + + do { + seq =3D read_seqcount_begin(&inode->i_size_seqcount); + *i_size =3D inode->i_size; + *remote_i_size =3D ictx->_remote_i_size; + *zero_point =3D ictx->_zero_point; + } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + *i_size =3D inode->i_size; + *remote_i_size =3D ictx->_remote_i_size; + *zero_point =3D ictx->_zero_point; + preempt_enable(); +#else + /* Pairs with smp_store_release() in i_size_write() */ + *i_size =3D smp_load_acquire(&inode->i_size); + /* Pairs with smp_store_release() in netfs_write_remote_i_size() */ + *remote_i_size =3D smp_load_acquire(&ictx->_remote_i_size); + /* Pairs with smp_store_release() in netfs_write_zero_point() */ + *zero_point =3D smp_load_acquire(&ictx->_zero_point); +#endif +} + +/* + * netfs_write_sizes - Set remote_i_size and zero_point safely + * @ictx: The inode context to access + * @remote_i_size: The new value for the size of the file on the server + * @zero_point: The new value for the point beyond which the server has no= data + * + * Set both remote_i_size and zero_point safely without the potential for + * tearing on 32-bit arches. + * + * NOTE: unlike netfs_read_zero_point(), netfs_write_zero_point() does need + * locking around it (normally i_rwsem), otherwise on 32bit/SMP an update = of + * i_size_seqcount can be lost, resulting in subsequent read calls spinning + * forever. + */ +static inline void netfs_write_sizes(struct netfs_inode *ictx, + unsigned long long remote_i_size, + unsigned long long zero_point) +{ +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + struct inode *inode =3D &ictx->inode; + + spin_lock(&inode->i_lock); + write_seqcount_begin(&inode->i_size_seqcount); + ictx->_remote_i_size =3D remote_i_size; + ictx->_zero_point =3D zero_point; + write_seqcount_end(&inode->i_size_seqcount); + spin_unlock(&inode->i_lock); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + ictx->_remote_i_size =3D remote_i_size; + ictx->_zero_point =3D zero_point; + preempt_enable(); +#else + /* + * Pairs with smp_load_acquire() in netfs_read_remote_i_size and + * netfs_read_zero_point() to ensure changes related to inode size + * (such as page contents) are visible before we see the changed inode + * size. + */ + smp_store_release(&ictx->_remote_i_size, remote_i_size); + smp_store_release(&ictx->_zero_point, zero_point); +#endif +} + /** * netfs_inode_init - Initialise a netfslib inode context * @ctx: The netfs inode to initialise @@ -488,8 +805,8 @@ static inline void netfs_inode_init(struct netfs_inode = *ctx, bool use_zero_point) { ctx->ops =3D ops; - ctx->remote_i_size =3D i_size_read(&ctx->inode); - ctx->zero_point =3D LLONG_MAX; + ctx->_remote_i_size =3D i_size_read(&ctx->inode); + ctx->_zero_point =3D LLONG_MAX; ctx->flags =3D 0; atomic_set(&ctx->io_count, 0); #if IS_ENABLED(CONFIG_FSCACHE) @@ -498,7 +815,7 @@ static inline void netfs_inode_init(struct netfs_inode = *ctx, mutex_init(&ctx->wb_lock); /* ->releasepage() drives zero_point */ if (use_zero_point) { - ctx->zero_point =3D ctx->remote_i_size; + ctx->_zero_point =3D ctx->_remote_i_size; mapping_set_release_always(ctx->inode.i_mapping); } } @@ -511,13 +828,40 @@ static inline void netfs_inode_init(struct netfs_inod= e *ctx, * * Inform the netfs lib that a file got resized so that it can adjust its = state. */ -static inline void netfs_resize_file(struct netfs_inode *ctx, loff_t new_i= _size, +static inline void netfs_resize_file(struct netfs_inode *ictx, + unsigned long long new_i_size, bool changed_on_server) { +#if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) + struct inode *inode =3D &ictx->inode; + + preempt_disable(); + write_seqcount_begin(&inode->i_size_seqcount); if (changed_on_server) - ctx->remote_i_size =3D new_i_size; - if (new_i_size < ctx->zero_point) - ctx->zero_point =3D new_i_size; + ictx->_remote_i_size =3D new_i_size; + if (new_i_size < ictx->_zero_point) + ictx->_zero_point =3D new_i_size; + write_seqcount_end(&inode->i_size_seqcount); + preempt_enable(); +#elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) + preempt_disable(); + if (changed_on_server) + ictx->_remote_i_size =3D new_i_size; + if (new_i_size < ictx->_zero_point) + ictx->_zero_point =3D new_i_size; + preempt_enable(); +#else + /* + * Pairs with smp_load_acquire() in netfs_read_remote_i_size and + * netfs_read_zero_point() to ensure changes related to inode size + * (such as page contents) are visible before we see the changed inode + * size. + */ + if (changed_on_server) + smp_store_release(&ictx->_remote_i_size, new_i_size); + if (new_i_size < ictx->_zero_point) + smp_store_release(&ictx->_zero_point, new_i_size); +#endif } =20 /** From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 A340B47CC75 for ; Tue, 28 Apr 2026 13:19:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382355; cv=none; b=iQqP5ETJdAP6JYabr+gUWzUFFlHHMuIhhqeLsj2XkVMKxCTA/rOyOXm5jHTMt6GopeTfWVuUQ1RsGbdQ5yZkGpwTrvNoH1QyDqYOqLS9eUQOPvqdbtbhdP4v5aeMEoqBkHb/+Eh2uuOUVN+dhepR2qMS8iH4yAacc5NtgiPzSmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382355; c=relaxed/simple; bh=kelRfB9HLCimHVHp5EACjpTSW1lun3yfZPdF/i143mw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mVYEyMSgC66t+LiXdIaMk/J0vym+fJxSOp/VK2plcstB1a4Lt9Ml8xyp9TChQsFecu5bJn/FG6pG4Xmp3P4jsx1pXj0nKkDauUkHI1MLCLwOBQQGo5DC/dnhH0RAnGcfirgYRS2LV+DyrB2tCrCJ3O4Xi6NteDY7Ca1t/Xh0JAE= 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=DWjYlmDA; arc=none smtp.client-ip=170.10.133.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="DWjYlmDA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382352; 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=RbFPc1DQP2QESNGAiyNusbxIP1Nhk9Foj9DJsGPjIqg=; b=DWjYlmDA6vxmb7eKJaDh2umNTq2x5f38LceyQh7cZQebxfBD7QMGrbDvCIlBC2LbN8WRXy 0GECJlkGJsUxJPENxj2DkbJjx43VsWkWZrEJaNhzvMYFcW9MpGah1DSbcOouu59Uo6eFbX e0OGfUursyv/XzwWwHGSb6T36MtsdwE= 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-134-7R7GzpzrMAKkf9XMaH2Oig-1; Tue, 28 Apr 2026 09:19:09 -0400 X-MC-Unique: 7R7GzpzrMAKkf9XMaH2Oig-1 X-Mimecast-MFC-AGG-ID: 7R7GzpzrMAKkf9XMaH2Oig_1777382347 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 8A5B11869195; Tue, 28 Apr 2026 13:18:32 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 75DF9195608E; Tue, 28 Apr 2026 13:18:29 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 06/24] netfs: Fix zeropoint update where i_size > remote_i_size Date: Tue, 28 Apr 2026 14:17:36 +0100 Message-ID: <20260428131756.922303-7-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Fix the update of the zero point[*] by netfs_release_folio() when there is uncommitted data in the pagecache beyond the folio being released but the on-server EOF is in this folio (ie. i_size > remote_i_size). The update needs to limit zero_point to remote_i_size, not i_size as i_size is a local phenomenon reflecting updates made locally to the pagecache, not stuff written to the server. remote_i_size tracks the server's i_size. [*] The zero point is the file position from which we can assume that the server will just return zeros, so we can avoid generating reads. Note that netfs_invalidate_folio() probably doesn't need fixing as zero_point should be updated by setattr after truncation or fallocate. Found with: fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=3Djunk.fsxops using the following as junk.fsxops: truncate 0x0 0x1bbae 0x82864 write 0x3ef2e 0xf9c8 0x1bbae write 0x67e05 0xcb5a 0x4e8f6 mapread 0x57781 0x85b6 0x7495f copy_range 0x5d3d 0x10329 0x54fac 0x7495f write 0x64710 0x1c2b 0x7495f mapread 0x64000 0x1000 0x7495f on cifs with the default cache option. It shows read-gaps on folio 0x64 failing with a short read (ie. it hits EOF) if the FMODE_READ check is commented out in netfs_perform_write(): if (//(file->f_mode & FMODE_READ) || netfs_is_cache_enabled(ctx)) { and no fscache. This was initially found with the generic/522 xfstest. Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netf= s_inval_folio()") Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/misc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 67e089fa0b0b..35f570e13e4a 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -299,9 +299,9 @@ bool netfs_release_folio(struct folio *folio, gfp_t gfp) return false; =20 netfs_read_sizes(ctx, &i_size, &remote_i_size, &zero_point); - end =3D umin(folio_next_pos(folio), i_size); + end =3D folio_next_pos(folio); if (end > zero_point) - netfs_write_zero_point(ctx, end); + netfs_push_back_zero_point(ctx, umin(end, remote_i_size)); =20 if (folio_test_private(folio)) return false; From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 D852646AEE8 for ; Tue, 28 Apr 2026 13:19:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382342; cv=none; b=RSzyKzDZp8NjklA0RTxC3CeKhxJ8TM9regA/42SNhMRgN/87vVnLehu23VfvCgIYxNtusFG10V1i+gPDbjo08qB591jn22/Zoc5WWvEJL8PiMXXjSYHjiyHp2tN4ZL1QMfaR9NN7fV8F+rd8F9Ai5exP+alT7/codYqKMCYdDyw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382342; c=relaxed/simple; bh=uiSuswE0HtjaEtg1ZPUSz30QNz2KPnD1R4oUG15gXaQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iXc2Y4Rcvp6+gBoA0cD8PBccOtjvzULIMxbx23pa2CPXH4gHxyXDOp3dTF+cxvVoNtC9h16spa6JUjvyGuoGeyBomAXsKYBk+65xx/yMe1fwSB+gie9zB6o9DVgUTJr41+lNSOc7brRD+W6UV38ZLI7Tt+A0fltaJASa5CHcehE= 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=LbwRo2kl; arc=none smtp.client-ip=170.10.133.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="LbwRo2kl" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382340; 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=fn5MwR0bkk2sYoCxMUvbMgZpxH4BijhOwUaadlW8+Aw=; b=LbwRo2kl3zZhf+MbZi7OF+EBn1SkfMG442KxVs2mSo9wNxMSm2g1WbQMWxMmN9IYY0/2yZ hPLyXxRSTHjJhqoPRGAW5wAvyaVWLzAJ2yd5Yq521PgQsWpXi+Uz9VB/pU82mzxCOSjl3h zJa6G5zUQ2nxOmDpQspzD1VzFY3rLY4= 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-544-Hbf_70TVN2SjlTbXL-m1hw-1; Tue, 28 Apr 2026 09:18:56 -0400 X-MC-Unique: Hbf_70TVN2SjlTbXL-m1hw-1 X-Mimecast-MFC-AGG-ID: Hbf_70TVN2SjlTbXL-m1hw_1777382333 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 C49F91955F03; Tue, 28 Apr 2026 13:18:37 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3E766195608E; Tue, 28 Apr 2026 13:18:34 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Viacheslav Dubeyko Subject: [PATCH v5 07/24] netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call Date: Tue, 28 Apr 2026 14:17:37 +0100 Message-ID: <20260428131756.922303-8-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" From: Viacheslav Dubeyko The multiple runs of generic/013 test-case is capable to reproduce a kernel BUG at mm/filemap.c:1504 with probability of 30%. while true; do sudo ./check generic/013 done [ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0= x10781 pfn:0x1c322 [ 9849.452412] memcg:ffff8881a1915800 [ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" [ 9849.452432] flags: 0x17ffffc0000000(node=3D0|zone=3D2|lastcpupid=3D0x1ff= fff) [ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff= 88816110d248 [ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff= 8881a1915800 [ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(foli= o)) [ 9849.452474] ------------[ cut here ]------------ [ 9849.452476] kernel BUG at mm/filemap.c:1504! [ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI [ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc= 1+ #18 PREEMPT(full) [ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS = 1.17.0-9.fc43 06/1 0/2025 [ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 = 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 2= 1 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 00000000000= 00000 [ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00000000000= 00000 [ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 00000000000= 00000 [ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 00000000107= 82000 [ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 00000000000= 01000 [ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:= 0000000000000000 [ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 00000000007= 72ef0 [ 9849.504459] PKRU: 55555554 [ 9849.504626] Call Trace: [ 9849.505242] [ 9849.505379] netfs_write_begin+0x7c8/0x10a0 [ 9849.505877] ? __kasan_check_read+0x11/0x20 [ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 [ 9849.507178] ceph_write_begin+0x8c/0x1c0 [ 9849.507934] generic_perform_write+0x391/0x8f0 [ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 [ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 [ 9849.509581] ? ceph_get_caps+0x63/0xf0 [ 9849.510259] ? ceph_get_caps+0x63/0xf0 [ 9849.510530] ceph_write_iter+0xe79/0x1ae0 [ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 [ 9849.511839] ? lock_acquire+0x1ad/0x310 [ 9849.512334] ? ksys_write+0xf9/0x230 [ 9849.512582] ? lock_is_held_type+0xaa/0x140 [ 9849.513128] vfs_write+0x512/0x1110 [ 9849.513634] ? __fget_files+0x33/0x350 [ 9849.513893] ? __pfx_vfs_write+0x10/0x10 [ 9849.514143] ? mutex_lock_nested+0x1b/0x30 [ 9849.514394] ksys_write+0xf9/0x230 [ 9849.514621] ? __pfx_ksys_write+0x10/0x10 [ 9849.514887] ? do_syscall_64+0x25e/0x1520 [ 9849.515122] ? __kasan_check_read+0x11/0x20 [ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.515655] __x64_sys_write+0x72/0xd0 [ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 [ 9849.516130] x64_sys_call+0x22f/0x2390 [ 9849.516341] do_syscall_64+0x12b/0x1520 [ 9849.516545] ? do_syscall_64+0x27c/0x1520 [ 9849.516783] ? do_syscall_64+0x27c/0x1520 [ 9849.517003] ? lock_release+0x318/0x480 [ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 [ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 [ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.518073] ? do_syscall_64+0x25e/0x1520 [ 9849.518291] ? __kasan_check_read+0x11/0x20 [ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.518799] ? do_syscall_64+0x27c/0x1520 [ 9849.519024] ? local_clock_noinstr+0xf/0x120 [ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.519544] ? do_syscall_64+0x25e/0x1520 [ 9849.519781] ? __kasan_check_read+0x11/0x20 [ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520273] ? do_syscall_64+0x27c/0x1520 [ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520767] ? irqentry_exit+0x10c/0x6c0 [ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 [ 9849.521224] ? exc_page_fault+0xab/0x130 [ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.521766] RIP: 0033:0x7e36cbd14907 [ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f = 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48= > 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 [ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 000000= 0000000001 [ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd= 14907 [ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 00000000000= 00004 [ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797e= c6370 [ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 00000000000= 00049 [ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 00000000000= 00000 [ 9849.525447] [ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_un= core_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class = intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_c= lmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw = vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_code= l rbd msr parport_pc ppdev lp parport efi_pstore [ 9849.529150] ---[ end trace 0000000000000000 ]--- [ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 = 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f= > 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 00000000000= 00000 [ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00000000000= 00000 [ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 00000000000= 00000 [ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 00000000107= 82000 [ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 00000000000= 01000 [ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:= 0000000000000000 [ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 00000000007= 72ef0 [ 9849.548679] PKRU: 55555554 The race sequence: 1. Read completes -> netfs_read_collection() runs 2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...) 3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin() 4. The netfs_unlock_abandoned_read_pages() unlocks the folio 5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO() The key reason of the issue that netfs_unlock_abandoned_read_pages() doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes folio_unlock() unconditionally. This patch implements in netfs_unlock_abandoned_read_pages() logic similar to netfs_unlock_read_folio(). Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Signed-off-by: Viacheslav Dubeyko Signed-off-by: David Howells Reviewed-by: Paulo Alcantara (Red Hat) cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org cc: Ceph Development --- fs/netfs/read_retry.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index 5ec548b996d6..e10eb5a07332 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -292,8 +292,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io= _request *rreq) struct folio *folio =3D folioq_folio(p, slot); =20 if (folio && !folioq_is_marked2(p, slot)) { - trace_netfs_folio(folio, netfs_folio_trace_abandon); - folio_unlock(folio); + if (folio->index =3D=3D rreq->no_unlock_folio && + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, + &rreq->flags)) { + _debug("no unlock"); + } else { + trace_netfs_folio(folio, + netfs_folio_trace_abandon); + folio_unlock(folio); + } } } } From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 5D6F647F2D3 for ; Tue, 28 Apr 2026 13:19:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382378; cv=none; b=FlKeMxsbgC56/J0mPJN9/bByYv1p2j/LnmjN+GdYdX0+x/OgMxmZRzA8P8frUID8hDYMMIb5/iLN7v0z6p8QA7aPHsvbbfJLrsn2XTEOmSijE3S6yOnS+x9y0d8e51hp/00JaPW4pR4ErCo9tRQUDYj8ZMypqnCmhkh1tlS3wAw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382378; c=relaxed/simple; bh=S62t+upgojIYhz1p6dALu+kyrentByS35lQhzLSLXbY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OREUllfb0rXxZkXMbuQUJsn3XMnpT/DQllEmWSB3xWdHVnmwsbtJK5mX/kfeOxvYaRS54NDa6JdYM/PTLRK2QvxdIpfvWT056RK8/UCfMcRRLZV84KbHvQ6yCXhQEYgkrZ7yPCWHZeuZzfNn5wUcmiIRPqaDWrIkfBQJLhoSTds= 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=ElLE8Iiw; arc=none smtp.client-ip=170.10.133.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="ElLE8Iiw" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382375; 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=0M8NeyGOtxqcK9n3lFvZBV03uT/fzw4I/4MxBJJ99Zo=; b=ElLE8Iiwr7nLOPEsG9N03f7EfliVWMXPTZaPWOfuDFBbQJ9pjalI53U5RUNWdlFd5WnA0c +NoTnJZ3dtD1Y8/d4Cb0MgvDtIyOEUc9/IWYXwmUPPaTfAEyb9bZL7oSpi4XxWytIFUsHX hsTukJf3eNcxvEcTqE7xqh6fsmNUNas= 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-74-ZSty4BcUOtSXjU4EqNIxmg-1; Tue, 28 Apr 2026 09:19:31 -0400 X-MC-Unique: ZSty4BcUOtSXjU4EqNIxmg-1 X-Mimecast-MFC-AGG-ID: ZSty4BcUOtSXjU4EqNIxmg_1777382369 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 A213118080B3; Tue, 28 Apr 2026 13:18:42 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7747E1800370; Tue, 28 Apr 2026 13:18:39 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Xiaoli Feng , stable@vger.kernel.org Subject: [PATCH v5 08/24] netfs: fix error handling in netfs_extract_user_iter() Date: Tue, 28 Apr 2026 14:17:38 +0100 Message-ID: <20260428131756.922303-9-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" From: Paulo Alcantara In netfs_extract_user_iter(), if iov_iter_extract_pages() failed to extract user pages, bail out on -ENOMEM, otherwise return the error code only if @npages =3D=3D 0, allowing short DIO reads and writes to be issued. This fixes mmapstress02 from LTP tests against CIFS. Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into= a BVEC iterator") Reported-by: Xiaoli Feng Signed-off-by: Paulo Alcantara (Red Hat) Signed-off-by: David Howells Cc: netfs@lists.linux.dev Cc: stable@vger.kernel.org Cc: linux-cifs@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org --- fs/netfs/iterator.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c index 154a14bb2d7f..adca78747f23 100644 --- a/fs/netfs/iterator.c +++ b/fs/netfs/iterator.c @@ -22,7 +22,7 @@ * * Extract the page fragments from the given amount of the source iterator= and * build up a second iterator that refers to all of those bits. This allo= ws - * the original iterator to disposed of. + * the original iterator to be disposed of. * * @extraction_flags can have ITER_ALLOW_P2PDMA set to request peer-to-pee= r DMA be * allowed on the pages extracted. @@ -67,8 +67,8 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, si= ze_t orig_len, ret =3D iov_iter_extract_pages(orig, &pages, count, max_pages - npages, extraction_flags, &offset); - if (ret < 0) { - pr_err("Couldn't get user pages (rc=3D%zd)\n", ret); + if (unlikely(ret <=3D 0)) { + ret =3D ret ?: -EIO; break; } =20 @@ -97,6 +97,13 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, s= ize_t orig_len, npages +=3D cur_npages; } =20 + if (ret < 0 && (ret =3D=3D -ENOMEM || npages =3D=3D 0)) { + for (i =3D 0; i < npages; i++) + unpin_user_page(bv[i].bv_page); + kvfree(bv); + return ret; + } + iov_iter_bvec(new, orig->data_source, bv, npages, orig_len - count); return npages; } From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 CD060477E32 for ; Tue, 28 Apr 2026 13:19:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382370; cv=none; b=o8ugrUpdHFQ8KKuBxieObITkjv5dcNVt6ta487XSP0L6MKI70X6cKJ4FZ+tH+lEqx14FSr8XUQLzIoi3YTMvKOms6yVMkxvjYglzDisRXnAv7FWIvAjwF9jwKu/wDz7HOtVggx7vW/rmxo+JZf4me8YBKaJLz9aqgOwe8tzQvWY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382370; c=relaxed/simple; bh=6aS16OhU7wJB8E9ZvNzMBoTTc64B5xcaExT2qrf2F8c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MWVozDfK1ap6LYZ7/5puP2hzHFZkX7xSV8FyhgGO7wucFkICtA04SRFJGCy6kf/e4YKcGyjZnBeBxaq+ThLu+EIHPDxafWB267/84dF1y1Ms9hxTXB62PntG3Yd0pBr8+wa9/HtZbIO53IpUgyEpftHyBGvKn4rNIIluOy+OzAA= 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=K95QODXs; arc=none smtp.client-ip=170.10.133.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="K95QODXs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382367; 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=bHSQahnA7js1P4paGsZUSZ9WNRs6QCvVJJSxiV11wQI=; b=K95QODXsW5HxGyT+mCNxzzodFOpE/3WaZO/Xa15/Cts4Dv9V+qDROhrql9UuzFEM1pYOtK mGfa/265adeyP5xRl2G3oQMvKFkBD2BitCeFSCkPU8A0yFpZRM9MO8MpMp4zC5ijTgBsZR gHDMc3rQa7VqoVbZUxI5nt4y0QHs8HE= 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-512-4t6aizAbP86H_MufK2aZGw-1; Tue, 28 Apr 2026 09:19:24 -0400 X-MC-Unique: 4t6aizAbP86H_MufK2aZGw-1 X-Mimecast-MFC-AGG-ID: 4t6aizAbP86H_MufK2aZGw_1777382362 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 0BC10186057E; Tue, 28 Apr 2026 13:18:47 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5653019560AB; Tue, 28 Apr 2026 13:18:44 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 09/24] netfs: Fix overrun check in netfs_extract_user_iter() Date: Tue, 28 Apr 2026 14:17:39 +0100 Message-ID: <20260428131756.922303-10-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills pages[], then those pages don't get included in the iterator constructed at the end of the function. If there was an overfill, memory corruption has already happened. Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into= a BVEC iterator") Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40r= edhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/iterator.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c index adca78747f23..932f745fa3b5 100644 --- a/fs/netfs/iterator.c +++ b/fs/netfs/iterator.c @@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, = size_t orig_len, break; } =20 - if (ret > count) { - pr_err("get_pages rc=3D%zd more than %zu\n", ret, count); + if (WARN(ret > count, + "%s: extract_pages overrun %zd > %zu bytes\n", + __func__, ret, count)) { + ret =3D -EIO; break; } =20 - count -=3D ret; - ret +=3D offset; - cur_npages =3D DIV_ROUND_UP(ret, PAGE_SIZE); - - if (npages + cur_npages > max_pages) { - pr_err("Out of bvec array capacity (%u vs %u)\n", - npages + cur_npages, max_pages); + cur_npages =3D DIV_ROUND_UP(offset + ret, PAGE_SIZE); + if (WARN(cur_npages > max_pages - npages, + "%s: extract_pages overrun %u > %u pages\n", + __func__, npages + cur_npages, max_pages)) { + ret =3D -EIO; break; } =20 + count -=3D ret; + ret +=3D offset; + for (i =3D 0; i < cur_npages; i++) { len =3D ret > PAGE_SIZE ? PAGE_SIZE : ret; bvec_set_page(bv + npages + i, *pages++, len - offset, offset); From nobody Thu May 7 05:50:19 2026 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 F150147279E for ; Tue, 28 Apr 2026 13:19:16 +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=1777382358; cv=none; b=qznmDA179ZJZxPpG/aHSm1V1/ry4HE9uPBtZrnr9YS2Bf0jdqAYx+ARW1KvkMjImMY8P0R08iiUA7D8yQSbv5SihA/Mz5iY6AZds0dBB+JGjdZ3kZakaweqKqCP/UKlnmsWntwOpgCBLaSUmg9BO2ac69mSI4O4YxzpXCAmYe2A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382358; c=relaxed/simple; bh=fxR0HRvSv22xXvXZ87M+p66ej2r+Sv2DAKvy6zA3uPg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GEvZMy9jgmoaWUpE2P9ia6O/zaYNLCsh6VJ3Hdeo82FKD8Or37KTTABHW0yqc9H27hlO3XrR/rWRJdUzVSWAdmYTLomoSd2af+yYqWY2DdpZk6Ef2UVV2VTYY9y8yuFjeRncd22YYE0eqNNUSXNhWakdQtbdILSDErLebcFiBDg= 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=P3FO1P1U; 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="P3FO1P1U" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382355; 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=VAlXvNFC3a/D86PNh8eSdyFG14ll2LJIPxlUeY75MuY=; b=P3FO1P1Uxi7+7MPUVv5cF2K63zK1XSVkZutmagTZXWsDtfHtMwDpL5pB699d6ymfpJbE0k z03B8Re96skGk0POQHQUz2zyeYIOkyM9VryidpLRQlLa9YyHiJkD/+fXV9paN0UUGB/8tE wimxs/0cEiEcx2pktQ5hx0cIfbv69TA= 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-661-DFDOW-0ZOJKBfmUktfd99Q-1; Tue, 28 Apr 2026 09:19:13 -0400 X-MC-Unique: DFDOW-0ZOJKBfmUktfd99Q-1 X-Mimecast-MFC-AGG-ID: DFDOW-0ZOJKBfmUktfd99Q_1777382351 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 AF94C18AB432; Tue, 28 Apr 2026 13:18:51 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B34761800446; Tue, 28 Apr 2026 13:18:48 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 10/24] netfs: Fix potential uninitialised var in netfs_extract_user_iter() Date: Tue, 28 Apr 2026 14:17:40 +0100 Message-ID: <20260428131756.922303-11-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" In netfs_extract_user_iter(), if it's given a zero-length iterator, it will fall through the loop without setting ret, and so the error handling behaviour will be undefined, depending on whether ret happens to be negative. The value of ret then propagates back up the callstack. Fix this by presetting ret to 0. Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into= a BVEC iterator") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/iterator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c index 932f745fa3b5..8b70d9567739 100644 --- a/fs/netfs/iterator.c +++ b/fs/netfs/iterator.c @@ -43,7 +43,7 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, si= ze_t orig_len, unsigned int max_pages; unsigned int npages =3D 0; unsigned int i; - ssize_t ret; + ssize_t ret =3D 0; size_t count =3D orig_len, offset, len; size_t bv_size, pg_size; From nobody Thu May 7 05:50:19 2026 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 1588C47DD5F for ; Tue, 28 Apr 2026 13:19:28 +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=1777382369; cv=none; b=QzonQ5ti0SZy3VSE0dDQRYPA89AW8Nd83B2RbgsQbl9e8k1DDDwaICjOlGkFF5mdhrPn1QXUl1m1Yyx4vk5tF4VOj4LuyedD4g6itD/xqsyGpDd7TckiNgxS6tnp681Z8L3uNNwk73J4sPipvpe1F+OadNA9h+2MhbJPH5Qdx4g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382369; c=relaxed/simple; bh=/O06LoIDfiOtWAPZrJ3+n/2LERmqUD95k/r1rfAncFU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DrbaOlJv4OoGpt1G2BnLtCZM60WCZ36JdM9ygG4tYEgy+0/vYWGKqOg1210Z8IDy8xPiX7k4p+4KmCJZL5SEPc4boTp78I/YQp7UrJY6vvXCeZg/GFnJEnHAAovxnxFuJ6Th78lNR+h5twjXWMNojghMfyb+WZ9bN4QZuDuSBxc= 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=aDs1iznZ; 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="aDs1iznZ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382367; 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=nEFqpiDn1G3xrxrigrHLzfZg3EiC1oCTBoGBP+9EHr4=; b=aDs1iznZK/hDfVE4UD7dZPM1AeLqwOWpACDAnjD8DtgtJRegnNtVJ+rGDY0iZfwRWzScdt u9nqH5CkQnX9YnYO3a0usnOZ+Pc+XKZoOSneDV1KcxiBPVDmSs1iKDQeNnIriFkOe/ner5 Jh0Tw2JFGMCreoD6hrSwnnS93eGnmMY= 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-449--L_gYyxBN0CD6BPz0P8M5w-1; Tue, 28 Apr 2026 09:19:21 -0400 X-MC-Unique: -L_gYyxBN0CD6BPz0P8M5w-1 X-Mimecast-MFC-AGG-ID: -L_gYyxBN0CD6BPz0P8M5w_1777382360 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 EF5AA195606B; Tue, 28 Apr 2026 13:18:56 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 65896195608E; Tue, 28 Apr 2026 13:18:53 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Marc Dionne , Matthew Wilcox Subject: [PATCH v5 11/24] netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone Date: Tue, 28 Apr 2026 14:17:41 +0100 Message-ID: <20260428131756.922303-12-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" If a streaming write is made, this will leave the relevant modified folio in a not-uptodate, but dirty state with a netfs_folio struct hung off of folio->private indicating the dirty range. Subsequently truncating the file such that the dirty data in the folio is removed, but the first part of the folio theoretically remains will cause the netfs_folio struct to be discarded... but will leave the dirty flag set. If the folio is then read via mmap(), netfs_read_folio() will see that the page is dirty and jump to netfs_read_gaps() to fill in the missing bits. netfs_read_gaps(), however, expects there to be a netfs_folio struct present and can oops because truncate removed it. Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the event that all the dirty data in the folio is erased (as nfs does). Also add some tracepoints to log modifications to a dirty page. This can be reproduced with something like: dd if=3D/dev/zero of=3D/xfstest.test/foo bs=3D1M count=3D1 umount /xfstest.test mount /xfstest.test xfs_io -c "w 0xbbbf 0xf96c" \ -c "truncate 0xbbbf" \ -c "mmap -r 0xb000 0x11000" \ -c "mr 0xb000 0x11000" \ /xfstest.test/foo with fscaching disabled (otherwise streaming writes are suppressed) and a change to netfs_perform_write() to disallow streaming writes if the fd is open O_RDWR: if (//(file->f_mode & FMODE_READ) || <--- comment this out netfs_is_cache_enabled(ctx)) { It should be reproducible even without this change, but if prevents the above trivial xfs_io command from reproducing it. Note that the initial dd is important: the file must start out sufficiently large that the zero-point logic doesn't just clear the gaps because it knows there's nothing in the file to read yet. Unmounting and mounting is needed to clear the pagecache (there are other ways to do that that may also work). This was initially reproduced with the generic/522 xfstest on some patches that remove the FMODE_READ restriction. Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping = and streaming write") Reported-by: Marc Dionne Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/misc.c | 6 +++++- include/trace/events/netfs.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 35f570e13e4a..9fd41a97f83d 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -256,6 +256,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t= offset, size_t length) /* Move the start of the data. */ finfo->dirty_len =3D fend - iend; finfo->dirty_offset =3D offset; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); return; } =20 @@ -264,12 +265,14 @@ void netfs_invalidate_folio(struct folio *folio, size= _t offset, size_t length) */ if (iend >=3D fend) { finfo->dirty_len =3D offset - fstart; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_tail); return; } =20 /* A partial write was split. The caller has already zeroed * it, so just absorb the hole. */ + trace_netfs_folio(folio, netfs_folio_trace_invalidate_middle); } return; =20 @@ -277,8 +280,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t= offset, size_t length) netfs_put_group(netfs_folio_group(folio)); folio_detach_private(folio); folio_clear_uptodate(folio); + folio_cancel_dirty(folio); kfree(finfo); - return; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_all); } EXPORT_SYMBOL(netfs_invalidate_folio); =20 diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 8c936fc575d5..0b702f74aefe 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -194,6 +194,10 @@ EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ + EM(netfs_folio_trace_invalidate_all, "inval-all") \ + EM(netfs_folio_trace_invalidate_front, "inval-front") \ + EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ + EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ EM(netfs_folio_trace_kill, "kill") \ EM(netfs_folio_trace_kill_cc, "kill-cc") \ EM(netfs_folio_trace_kill_g, "kill-g") \ From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 386BF477E27 for ; Tue, 28 Apr 2026 13:19:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382372; cv=none; b=cLFJfZo163uv0Wuwdf18KOvbCAbdp0UoZ4mOl4bFlhnw1Ox/eyTJEMnUWaJVQMJ5MDGgfFnYZMwpMe8RQwGjyg2DSbB/QYdcR0LOug5JlW8f9JK+Qd5he/DpCQB68K4ug9fqtm9uKykX+5Re3Wqcnz5XjUlFaHl64Vnu75S3PwQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382372; c=relaxed/simple; bh=Hls64bsLlj6noJlM3TRUh03Fmmi4w1KwMCcwqEYCqZ4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZguzFt6xLE75UFqQcV2Oy+yCi3APx7rOdZJTseEttL28rUh82EjMnJWoVPCXQHM82o7dwFXnfCcEw3rTfFSmFvcDoDAhYxV75nNe8qyVixe9VzwOzG0D1YSmiCIZj6NFzZ3nnWwnV377vBNqxfCnezAYf03UVIHRcwpvOFquqKI= 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=MmqXiJs+; arc=none smtp.client-ip=170.10.133.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="MmqXiJs+" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382368; 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=+l6afX5dOckJckSkiMhYLh2ewUrSBegiqJdH6fYSD7g=; b=MmqXiJs+a/RvwzQ5ecPOFyeEEJiZq1nfMHeM1bxK3bfswRpWc6A3xob+srOw5I5zT8Ly2m GHt/RBjQky0geredJxk779adCyQCXS9Qqltd+IxcAcVzIe3vN/XHKBXwE4tW5NPwhqWpYb b6V8eck98Qjpe6nEOac5kFLCe2MIpn4= 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-674-zukhUyAOP1yT7An13mJ-dw-1; Tue, 28 Apr 2026 09:19:24 -0400 X-MC-Unique: zukhUyAOP1yT7An13mJ-dw-1 X-Mimecast-MFC-AGG-ID: zukhUyAOP1yT7An13mJ-dw_1777382363 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 B59FE1979047; Tue, 28 Apr 2026 13:19:01 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A393B3000C22; Tue, 28 Apr 2026 13:18:58 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 12/24] netfs: Defer the emission of trace_netfs_folio() Date: Tue, 28 Apr 2026 14:17:42 +0100 Message-ID: <20260428131756.922303-13-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Change netfs_perform_write() to keep the netfs_folio trace value in a variable and emit it later to make it easier to choose the value displayed. This is a prerequisite for a subsequent patch. Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_write.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index fc94eb1ef27b..7ac128d0b4e5 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -149,6 +149,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, } =20 do { + enum netfs_folio_trace trace; struct netfs_folio *finfo; struct netfs_group *group; unsigned long long fpos; @@ -222,7 +223,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, if (unlikely(copied =3D=3D 0)) goto copy_failed; netfs_set_group(folio, netfs_group); - trace_netfs_folio(folio, netfs_folio_is_uptodate); + trace =3D netfs_folio_is_uptodate; goto copied; } =20 @@ -238,7 +239,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, folio_zero_segment(folio, offset + copied, flen); __netfs_set_group(folio, netfs_group); folio_mark_uptodate(folio); - trace_netfs_folio(folio, netfs_modify_and_clear); + trace =3D netfs_modify_and_clear; goto copied; } =20 @@ -256,7 +257,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, } __netfs_set_group(folio, netfs_group); folio_mark_uptodate(folio); - trace_netfs_folio(folio, netfs_whole_folio_modify); + trace =3D netfs_whole_folio_modify; goto copied; } =20 @@ -283,7 +284,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, if (unlikely(copied =3D=3D 0)) goto copy_failed; netfs_set_group(folio, netfs_group); - trace_netfs_folio(folio, netfs_just_prefetch); + trace =3D netfs_just_prefetch; goto copied; } =20 @@ -297,7 +298,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, if (offset =3D=3D 0 && copied =3D=3D flen) { __netfs_set_group(folio, netfs_group); folio_mark_uptodate(folio); - trace_netfs_folio(folio, netfs_streaming_filled_page); + trace =3D netfs_streaming_filled_page; goto copied; } =20 @@ -312,7 +313,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, finfo->dirty_len =3D copied; folio_attach_private(folio, (void *)((unsigned long)finfo | NETFS_FOLIO_INFO)); - trace_netfs_folio(folio, netfs_streaming_write); + trace =3D netfs_streaming_write; goto copied; } =20 @@ -332,9 +333,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, folio_detach_private(folio); folio_mark_uptodate(folio); kfree(finfo); - trace_netfs_folio(folio, netfs_streaming_cont_filled_page); + trace =3D netfs_streaming_cont_filled_page; } else { - trace_netfs_folio(folio, netfs_streaming_write_cont); + trace =3D netfs_streaming_write_cont; } goto copied; } @@ -350,6 +351,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, continue; =20 copied: + trace_netfs_folio(folio, trace); flush_dcache_folio(folio); =20 /* Update the inode size if we moved the EOF marker */ From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 ABA1747D921 for ; Tue, 28 Apr 2026 13:19:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382358; cv=none; b=ExiprorPruU2eVhEYZigycSSb53rq5rAgss4cFUbdMn+Nktcx2DdkVYNdZIpeZcSgypf2HTLbLcFCsIbV2WF6DQvZfEclDHBdsceTlfhzySWl4HddoBChKLKU8rVFkCpmB54G6U7YkbKj4JnCmNpRdgMhHvB1VOwfMcsCXc7/lI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382358; c=relaxed/simple; bh=3qCrcikapOBjDiKHYNn87mJHSh0bvZvMd8FSy9FXXaw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LPeBzaS3yPjQOwZVVWkMnI9fUxT/pS7ehG4fhmNfjrkOLb5+s/h+Xkdbd6g/rjSwjMweiXQscd0zVsP7hn0JevCm/80s6zQmC7FBQXBdoNxAlwLJruv00qACC6jR28qoYaiRqTictNYJY7cF6/aNDU9IDAstxhHwzv4cfKpL6NA= 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=Qiv6/HGP; arc=none smtp.client-ip=170.10.133.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="Qiv6/HGP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382355; 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=3o0bVWAsa0tr+4f6EBlhwvSVQai01iQefbnN9wRCFMY=; b=Qiv6/HGPappOM7aDVFHKDiXHgRuBTyqAVVU3U6t1pLT+heAlFtishalf1G1w93D+u0JcEJ Pe4NPx/7a+X9VE8dSv90OYORliyXis4NGzjLmWVcw0R1n4j1dqbXGL5ZmUK1but+4uiLPN ufFbU43YH1Cz1y0km25HlTfOWRySNJc= 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-448-sbygBWJ8NRWifB3zeK4WeA-1; Tue, 28 Apr 2026 09:19:12 -0400 X-MC-Unique: sbygBWJ8NRWifB3zeK4WeA-1 X-Mimecast-MFC-AGG-ID: sbygBWJ8NRWifB3zeK4WeA_1777382351 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 C3A921955DB7; Tue, 28 Apr 2026 13:19:06 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 743D6180047F; Tue, 28 Apr 2026 13:19:03 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 13/24] netfs: Fix streaming write being overwritten Date: Tue, 28 Apr 2026 14:17:43 +0100 Message-ID: <20260428131756.922303-14-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" In order to avoid reading whilst writing, netfslib will allow "streaming writes" in which dirty data is stored directly into folios without reading them first. Such folios are marked dirty but may not be marked uptodate. If a folio is entirely written by a streaming write, uptodate will be set, otherwise it will have a netfs_folio struct attached to ->private recording the dirty region. In the event that a partially written streaming write page is to be overwritten entirely by a single write(), netfs_perform_write() will try to copy over it, but doesn't discard the netfs_folio if it succeeds; further, it doesn't correctly handle a partial copy that overwrites some of the dirty data. Fix this by the following: (1) If the folio is successfully overwritten, free the netfs_folio struct before marking the page uptodate. (2) If the copy to the folio partially fails, but short of the dirty data, just ignore the copy. (3) If the copy partially fails and overwrites some of the dirty data, accept the copy, update the netfs_folio struct to record the new data. If the folio is now filled, free the netfs_folio and set uptodate, otherwise return a partial write. Found with: fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=3Djunk.fsxops using the following as junk.fsxops: truncate 0x0 0 0x927c0 write 0x63fb8 0x53c8 0 copy_range 0xb704 0x19b9 0x24429 0x79380 write 0x2402b 0x144a2 0x90660 * write 0x204d5 0x140a0 0x927c0 * copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * read 0x00000 0x20000 0x9157c read 0x20000 0x20000 0x9157c read 0x40000 0x20000 0x9157c read 0x60000 0x20000 0x9157c read 0x7e1a0 0xcfb9 0x9157c on cifs with the default cache option. It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write(): if (//(file->f_mode & FMODE_READ) || netfs_is_cache_enabled(ctx)) { and no fscache. This was initially found with the generic/522 xfstest. Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs= _perform_write()") Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_write.c | 47 ++++++++++++++++++++++++++---------- include/trace/events/netfs.h | 3 +++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 7ac128d0b4e5..25571a570ac9 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -246,18 +246,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struc= t iov_iter *iter, /* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset =3D=3D 0 && part >=3D flen) { copied =3D copy_folio_from_iter_atomic(folio, offset, part, iter); - if (unlikely(copied =3D=3D 0)) + if (likely(copied =3D=3D part)) { + if (finfo) { + trace =3D netfs_whole_folio_modify_filled; + goto folio_now_filled; + } + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); + trace =3D netfs_whole_folio_modify; + goto copied; + } + if (copied =3D=3D 0) goto copy_failed; - if (unlikely(copied < part)) { + if (!finfo || copied <=3D finfo->dirty_offset) { maybe_trouble =3D true; iov_iter_revert(iter, copied); copied =3D 0; folio_unlock(folio); goto retry; } - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); - trace =3D netfs_whole_folio_modify; + + /* We overwrote some existing dirty data, so we have to + * accept the partial write. + */ + finfo->dirty_len +=3D finfo->dirty_offset; + if (finfo->dirty_len =3D=3D flen) { + trace =3D netfs_whole_folio_modify_filled_efault; + goto folio_now_filled; + } + if (copied > finfo->dirty_len) + finfo->dirty_len =3D copied; + finfo->dirty_offset =3D 0; + trace =3D netfs_whole_folio_modify_efault; goto copied; } =20 @@ -327,16 +347,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struc= t iov_iter *iter, goto copy_failed; finfo->dirty_len +=3D copied; if (finfo->dirty_offset =3D=3D 0 && finfo->dirty_len =3D=3D flen) { - if (finfo->netfs_group) - folio_change_private(folio, finfo->netfs_group); - else - folio_detach_private(folio); - folio_mark_uptodate(folio); - kfree(finfo); trace =3D netfs_streaming_cont_filled_page; - } else { - trace =3D netfs_streaming_write_cont; + goto folio_now_filled; } + trace =3D netfs_streaming_write_cont; goto copied; } =20 @@ -350,6 +364,13 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct= iov_iter *iter, goto out; continue; =20 + folio_now_filled: + if (finfo->netfs_group) + folio_change_private(folio, finfo->netfs_group); + else + folio_detach_private(folio); + folio_mark_uptodate(folio); + kfree(finfo); copied: trace_netfs_folio(folio, trace); flush_dcache_folio(folio); diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 0b702f74aefe..aa9940ba307b 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -177,6 +177,9 @@ EM(netfs_folio_is_uptodate, "mod-uptodate") \ EM(netfs_just_prefetch, "mod-prefetch") \ EM(netfs_whole_folio_modify, "mod-whole-f") \ + EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ + EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ + EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ EM(netfs_streaming_write, "mod-streamw") \ EM(netfs_streaming_write_cont, "mod-streamw+") \ From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 DED1447DD79 for ; Tue, 28 Apr 2026 13:19:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382373; cv=none; b=J3Od3Gp97LkIeAzJgvwZ6tJ2KTSSLrzy33n7tBMSYeWitJvyyCWqskZQsOBoCtfhaO18M/ZVEUC2SkB/VG6z/VPPE1TkQqZ+aDBvf2P3bEC6Wn1vI9GaTZP0X7laNUASYLW1m8NM7TUVr/XalhxzCELsBi+4IFvFFQcZIY1DgYs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382373; c=relaxed/simple; bh=PhAVIry4RH7jejSdAGIhxh3RBLwcrsEnhZBSvVNQ6sU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Y0mVEayw/OAhYEpx3JtBJcbxVUa4b8dSa87EncoS5BYfwwFWGJUigmQLOopflXLXOb1cHDnhcPAxKiy8h4mp48PlpdZW0qNrFNtsIUWpOz6iSHCy3LgPcw3oPBMHxLlSACoFNqlT69o+tpxVMUE0fnqbT6/DE+c5N3o86PaKRpE= 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=RZmOQk9t; arc=none smtp.client-ip=170.10.133.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="RZmOQk9t" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382369; 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=YHPf6hGUWYEe9rJRq40F95gNcQEzsSyVW38x9qecVoo=; b=RZmOQk9tyByspPW9EXkzT+M/WnXeldzRPq+c9Kj3IgKxYv6okAvvizfNVJgQ+U/ZyD5xKw WTjIZcWhOEXJXGDj3jy7dc+ZU3hn8u4wQvvGD2u6rcZu7c2kSOi4ZKYQq7x7ENtPoZxsNR r9N8PZ7Sxm99jUZ20dOLOZ5c0pGU99w= 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-222-Rm4Xk0pOPTi4hjsMKKIbFw-1; Tue, 28 Apr 2026 09:19:26 -0400 X-MC-Unique: Rm4Xk0pOPTi4hjsMKKIbFw-1 X-Mimecast-MFC-AGG-ID: Rm4Xk0pOPTi4hjsMKKIbFw_1777382363 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 17465195DE6C; Tue, 28 Apr 2026 13:19:11 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 60A92180047F; Tue, 28 Apr 2026 13:19:08 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 14/24] netfs: Fix potential deadlock in write-through mode Date: Tue, 28 Apr 2026 14:17:44 +0100 Message-ID: <20260428131756.922303-15-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Fix netfs_advance_writethrough() to always unlock the supplied folio. It's been marked for writeback and the caller is holding both inode->i_rwsem and ictx->wb_lock, so there shouldn't be any danger of truncation, migration, fallocation, flushing or other I/O interfering whilst the folio is unlocked. Note that this may be easier to deal with once the queuing of folios is spl= it from the generation of subrequests. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40r= edhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/write_issue.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index b0e9690bb90c..0b07ebecb157 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -665,8 +665,10 @@ int netfs_advance_writethrough(struct netfs_io_request= *wreq, struct writeback_c } =20 wreq->len +=3D copied; - if (!to_page_end) + if (!to_page_end) { + folio_unlock(folio); return 0; + } =20 *writethrough_cache =3D NULL; return netfs_write_folio(wreq, wbc, folio); @@ -683,8 +685,10 @@ ssize_t netfs_end_writethrough(struct netfs_io_request= *wreq, struct writeback_c =20 _enter("R=3D%x", wreq->debug_id); =20 - if (writethrough_cache) + if (writethrough_cache) { + folio_lock(writethrough_cache); netfs_write_folio(wreq, wbc, writethrough_cache); + } =20 netfs_end_issue_write(wreq); From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 7D03348125D for ; Tue, 28 Apr 2026 13:20:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382403; cv=none; b=h/Y2Mkvsatlrnh8cezSgXEvVW+kidbYMNLAq5RTd92cvPeAoAX+iVevnygTFMl2KDP1jb9Ob/u2y3Ii+GejUcsdbzmyLey1DjIO9tHcrOs26U/qt2AtfTXVda0RSdIypqtWXNtGANHwXM+jNeXiQqbB6z6THrMF/tZwmbChhxd8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382403; c=relaxed/simple; bh=J8QawrkDmZnEn3V+onuYqCDz4wNl7JY0T6BmQPWssTg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=vDKAmrIGVxreF1+d5RUVArN2o+JUcUigrZynIwnS7QcK9riow3VO6G6GNQv6ixLq3X2LfEaL9hfiqPvL3O4A6Piy0315l+WPwnnh9hviXYw2LnmX29h+oENTnm6/bjO/km23Xm19Lqo+f0teg/pPQOhhbZ+YciIddLOkpFIjWQg= 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=N4ChkRUy; arc=none smtp.client-ip=170.10.133.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="N4ChkRUy" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382401; 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=zlc+rWHho9Uqkbw6bMnekEiU8RB1DEnYBoY4FdYh6bg=; b=N4ChkRUyzklS2+bQiXzrzojuc54TeQxbPyossdmGAjdLn+xB32mClh8PdGCOHeTO6QWNAv mExeo15yHc/YiLC63NAGmx5nBZBPuStfUbISeatDLFKsiR/zyrT6MeI/rkZN/qdz1KQx4Y b1OLyV9hXU8nDsIxNzZxv53MqDW9ufQ= 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-602-_13Brou9PSerEUX30fng9w-1; Tue, 28 Apr 2026 09:19:58 -0400 X-MC-Unique: _13Brou9PSerEUX30fng9w-1 X-Mimecast-MFC-AGG-ID: _13Brou9PSerEUX30fng9w_1777382396 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 9987418C1054; Tue, 28 Apr 2026 13:19:15 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A7933195608E; Tue, 28 Apr 2026 13:19:12 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 15/24] netfs: Fix read-gaps to remove netfs_folio from filled folio Date: Tue, 28 Apr 2026 14:17:45 +0100 Message-ID: <20260428131756.922303-16-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Fix netfs_read_gaps() to remove the netfs_folio record from the folio record before marking the folio uptodate if it successfully fills the gaps around the dirty data in a streaming write folio (dirty, but not uptodate). Found with: fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=3Djunk.fsxops using the following as junk.fsxops: truncate 0x0 0x138b1 0x8b15d * write 0x507ee 0x10df7 0x927c0 write 0x19993 0x10e04 0x927c0 * mapwrite 0x66214 0x1a253 0x927c0 copy_range 0xb704 0x89b9 0x24429 0x79380 write 0x2402b 0x144a2 0x90660 * mapwrite 0x204d5 0x140a0 0x927c0 * copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * read 0 0x9157c 0x9157c on cifs with the default cache option. It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write(): if (//(file->f_mode & FMODE_READ) || netfs_is_cache_enabled(ctx)) { and no fscache. This was initially found with the generic/522 xfstest. Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") --- fs/netfs/buffered_read.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index c0fe6dc40d87..c0a7f9e60f0c 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -395,6 +395,7 @@ static int netfs_read_gaps(struct file *file, struct fo= lio *folio) { struct netfs_io_request *rreq; struct address_space *mapping =3D folio->mapping; + struct netfs_group *group =3D netfs_folio_group(folio); struct netfs_folio *finfo =3D netfs_folio_info(folio); struct netfs_inode *ctx =3D netfs_inode(mapping->host); struct folio *sink =3D NULL; @@ -461,6 +462,12 @@ static int netfs_read_gaps(struct file *file, struct f= olio *folio) =20 ret =3D netfs_wait_for_read(rreq); if (ret >=3D 0) { + if (group) + folio_change_private(folio, group); + else + folio_detach_private(folio); + kfree(finfo); + trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); flush_dcache_folio(folio); folio_mark_uptodate(folio); } @@ -496,10 +503,8 @@ int netfs_read_folio(struct file *file, struct folio *= folio) struct netfs_inode *ctx =3D netfs_inode(mapping->host); int ret; =20 - if (folio_test_dirty(folio)) { - trace_netfs_folio(folio, netfs_folio_trace_read_gaps); + if (folio_test_dirty(folio)) return netfs_read_gaps(file, folio); - } =20 _enter("%lx", folio->index); From nobody Thu May 7 05:50:19 2026 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 BF173480DF5 for ; Tue, 28 Apr 2026 13:19:55 +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=1777382397; cv=none; b=rqY3BaHUwBVkabc+SM84/VZorJgKWMHalZ/TK98WfBuKaCze9qk3lI1V8g4Za+/tPYh+BPy6RXVJg2xhzyQfyHDNECnYX7NG53aMN7JExJjAyZx1KdSoby/W9Q9+E/Uv5qmIqeII8CCILDPUDv6zO4CxE+tPW4eknaDYWo/dyWc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382397; c=relaxed/simple; bh=lShWzYTWI8ZUl/3TWOlKibNVqtrcytr9UqfpImgWSR8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cDeLGJYkdh3egPdaQYutUwNSPVnZfmlV8vxgWM2ClvnUzsvdVr4QzIfD015u0ZUe0hfMKTP2/YJcDQVkXKFTeQmeyoJm7bx3tNI/Ye5Iuyqp19rGz6P7OPwK57iXTS1+EwM5Itmu+TtrTwNyQxG0hh27vZH7LX+aE+uYaxYC+x0= 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=EQlj3h+w; 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="EQlj3h+w" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382394; 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=TrTX3198kpCCtDQYOVWR0o7cIHQgx0i2zjy1/WuKGSY=; b=EQlj3h+w86vE6usiOzkAQTF3pwT2tYEcf9UpDQ0zxANJtY+u7kUxzMEoYUhsahpXOmjf1G GzqD6rGDOrikcswH7+A06a5kwrYv/4ZM6YMmUTEfM2DwRODFARE1EQGQAxDK0OCD7rNBZ5 LixmFgh/gjrvdR70Q8bwSNKqv3xba6g= 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-359-6l-1WpSePNiY0FvjE4FSxA-1; Tue, 28 Apr 2026 09:19:46 -0400 X-MC-Unique: 6l-1WpSePNiY0FvjE4FSxA-1 X-Mimecast-MFC-AGG-ID: 6l-1WpSePNiY0FvjE4FSxA_1777382383 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 246C4185A926; Tue, 28 Apr 2026 13:19:20 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 357AA195608E; Tue, 28 Apr 2026 13:19:16 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 16/24] netfs: Fix write streaming disablement if fd open O_RDWR Date: Tue, 28 Apr 2026 14:17:46 +0100 Message-ID: <20260428131756.922303-17-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" In netfs_perform_write(), "write streaming" (the caching of dirty data in dirty but !uptodate folios) is performed to avoid the need to read data that is just going to get immediately overwritten. However, this is/will be disabled in three circumstances: if the fd is open O_RDWR, if fscache is in use (as we need to round out the blocks for DIO) or if content encryption is enabled (again for rounding out purposes). The idea behind disabling it if the fd is open O_RDWR is that we'd need to flush the write-streaming page before we could read the data, particularly through mmap. But netfs now fills in the gaps if ->read_folio() is called on the page, so that is unnecessary. Further, this doesn't actually work if a separate fd is open for reading. Fix this by removing the check for O_RDWR, thereby allowing streaming writes even when we might read. This caused a number of problems with the generic/522 xfstest, but those are now fixed. Fixes: c38f4e96e605 ("netfs: Provide func to copy data to pagecache for buf= fered write") Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_write.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 25571a570ac9..11a8f64b1177 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -203,11 +203,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struc= t iov_iter *iter, } =20 /* Decide how we should modify a folio. We might be attempting - * to do write-streaming, in which case we don't want to a - * local RMW cycle if we can avoid it. If we're doing local - * caching or content crypto, we award that priority over - * avoiding RMW. If the file is open readably, then we also - * assume that we may want to read what we wrote. + * to do write-streaming, as we don't want to a local RMW cycle + * if we can avoid it. If we're doing local caching or content + * crypto, we award that priority over avoiding RMW. If the + * file is open readably, then we let ->read_folio() fill in + * the gaps. */ finfo =3D netfs_folio_info(folio); group =3D netfs_folio_group(folio); @@ -283,12 +283,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct= iov_iter *iter, =20 /* We don't want to do a streaming write on a file that loses * caching service temporarily because the backing store got - * culled and we don't really want to get a streaming write on - * a file that's open for reading as ->read_folio() then has to - * be able to flush it. + * culled. */ - if ((file->f_mode & FMODE_READ) || - netfs_is_cache_enabled(ctx)) { + if (netfs_is_cache_enabled(ctx)) { if (finfo) { netfs_stat(&netfs_n_wh_wstream_conflict); goto flush_content; From nobody Thu May 7 05:50:19 2026 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 3C56C477E22 for ; Tue, 28 Apr 2026 13:19:34 +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=1777382376; cv=none; b=IgL4H/7s1ByOu/8W4TU3PIPSw/avPdNMonJmfA4WUd40zaF6S2YcXLEYOQn2Z/KzdFfWXXn36JoFUs5gLJlbh5lAv/s2jV5ao48tbZnKoq33qE94jjr3njJ84mG/ysCD699IsKy/K7VKjRd2E5r+uk3Yp4vYzwXeMMlPBQh+ZnU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382376; c=relaxed/simple; bh=J6xbkiUXEBxqGua30Ly2BO3cTSDIQVPMlA1JsgMbaAg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k51p/GUwTMT08EJLWVTvCSnV7xtLr24q3SNvJ2d38UdE9NWXgwDoSJm/BZRzIeQkSB1yWaAs/Eslf9szhi0092Y72G4WQoY0btyzuV3v6BkQawgFSjd+WJXpY2PQI0u6cSb32EvC+kdR4RqNYyJYqtN0sK/x+iYAct0vVA/XEfU= 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=C71nKQV4; 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="C71nKQV4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382373; 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=zPw8vG2tI8x7LyUiAgQo8fFB7ZuQxgP71N5NQ3zP9Ok=; b=C71nKQV4SrTjfiGhSHyG5tYTZM1ZR+kXb/KkMzIvWAPIzCRVlS9vc2YpK8tvYSG9q9ND+4 ux7KEvaix94Xm8AV+ol5rA2Q/r+yPoilKXJLS3uE4L+WffemIziOmwOVWX94NbI0AyLLNd dJzcn8WCsHvQ31C0ZiMBtWGwr7qhFDo= 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-85-KETjkhGDMKu4F8BaFxma3A-1; Tue, 28 Apr 2026 09:19:27 -0400 X-MC-Unique: KETjkhGDMKu4F8BaFxma3A-1 X-Mimecast-MFC-AGG-ID: KETjkhGDMKu4F8BaFxma3A_1777382365 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 0F46F1955F25; Tue, 28 Apr 2026 13:19:25 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CC3251955F21; Tue, 28 Apr 2026 13:19:21 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Steve French , Matthew Wilcox Subject: [PATCH v5 17/24] netfs: Fix early put of sink folio in netfs_read_gaps() Date: Tue, 28 Apr 2026 14:17:47 +0100 Message-ID: <20260428131756.922303-18-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" Fix netfs_read_gaps() to release the sink page it uses after waiting for the request to complete. The way the sink page is used is that an ITER_BVEC-class iterator is created that has the gaps from the target folio at either end, but has the sink page tiled over the middle so that a single read op can fill in both gaps. The bug was found by KASAN detecting a UAF on the generic/075 xfstest in the cifsd kernel thread that handles reception of data from the TCP socket: BUG: KASAN: use-after-free in _copy_to_iter+0x48a/0xa20 Write of size 885 at addr ffff888107f92000 by task cifsd/1285 CPU: 2 UID: 0 PID: 1285 Comm: cifsd Not tainted 7.0.0 #6 PREEMPT(lazy) Call Trace: dump_stack_lvl+0x5d/0x80 print_report+0x17f/0x4f1 kasan_report+0x100/0x1e0 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x3c/0x60 _copy_to_iter+0x48a/0xa20 __skb_datagram_iter+0x2c9/0x430 skb_copy_datagram_iter+0x6e/0x160 tcp_recvmsg_locked+0xce0/0x1130 tcp_recvmsg+0xeb/0x300 inet_recvmsg+0xcf/0x3a0 sock_recvmsg+0xea/0x100 cifs_readv_from_socket+0x3a6/0x4d0 [cifs] cifs_read_iter_from_socket+0xdd/0x130 [cifs] cifs_readv_receive+0xaad/0xb10 [cifs] cifs_demultiplex_thread+0x1148/0x1740 [cifs] kthread+0x1cf/0x210 Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Reported-by: Steve French Signed-off-by: David Howells Reviewed-by: Paulo Alcantara (Red Hat) cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index c0a7f9e60f0c..18c8e00626c9 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -457,9 +457,6 @@ static int netfs_read_gaps(struct file *file, struct fo= lio *folio) =20 netfs_read_to_pagecache(rreq, NULL); =20 - if (sink) - folio_put(sink); - ret =3D netfs_wait_for_read(rreq); if (ret >=3D 0) { if (group) @@ -471,6 +468,9 @@ static int netfs_read_gaps(struct file *file, struct fo= lio *folio) flush_dcache_folio(folio); folio_mark_uptodate(folio); } + + if (sink) + folio_put(sink); folio_unlock(folio); netfs_put_request(rreq, netfs_rreq_trace_put_return); return ret < 0 ? ret : 0; From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 BF6AC47ECE1 for ; Tue, 28 Apr 2026 13:19:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382379; cv=none; b=tG2PI1tsx93/ijTxum64iFipBhlC/jMtWVNmmgeIgjDTN6DEOs2dVwldlqXyf3E/OCzKqCLRiQYUShfbqPZkUgXIkiR4R5CmnourFOL+GoXbb2Tvz5Mxw1lvarENQMPeHyrjqKLmBOqM/6JA3hFAfHuK61IHlNHp5S9G6V0TULI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382379; c=relaxed/simple; bh=JmXP+QXfysKowXIq/+dNx74WhS6n5x7XmBRvcwsYGOE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iNv1pH3dp+CclRFkSH8OlKs76uP//YY6ZmBSAakzlR8ZtUzNsPyYh8U4DFAbPhT+8xk7QgapVZ9D6Tz8E2xYi0Cs3NCtx8FmQRQAkWvo38as0hcDnHQwjowk0cquOiwJVE2n847lAy8maostp3njhGL3CzKj9d4/J0bI1QxtAyo= 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=PtAElIaR; arc=none smtp.client-ip=170.10.133.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="PtAElIaR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382376; 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=NVdup9Yb9JycCAuf4DYfAvqCMMZy27DYqgj2TZFqU1c=; b=PtAElIaR1Y7QKy/CJGDv6ZzYIyOaua9jKUg/OFvQ4IW1vA2Ge5fqMTIs1dyvBzrVyBOGhD tTdw0YGMvJ6CrSQ0GowcOgfG5p3ZHHzlcHrRqjJgKRFkANrD9VyMPXEyyFQmBhvvvlS/hI wKWv2Eqrmhu63jpfvSMTZoRBA5+s5sU= 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-652-MMscdLOhOGKsXqAJlrHUMw-1; Tue, 28 Apr 2026 09:19:31 -0400 X-MC-Unique: MMscdLOhOGKsXqAJlrHUMw-1 X-Mimecast-MFC-AGG-ID: MMscdLOhOGKsXqAJlrHUMw_1777382369 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 A1A041955E9C; Tue, 28 Apr 2026 13:19:29 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A608B180047F; Tue, 28 Apr 2026 13:19:26 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 18/24] netfs: Fix leak of request in netfs_write_begin() error handling Date: Tue, 28 Apr 2026 14:17:48 +0100 Message-ID: <20260428131756.922303-19-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Fix netfs_write_begin() to not leak our ref on the request in the event that we get an error from netfs_wait_for_read(). Fixes: 4090b31422a6 ("netfs: Add a function to consolidate beginning a read= ") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 18c8e00626c9..db279124d450 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -687,9 +687,9 @@ int netfs_write_begin(struct netfs_inode *ctx, =20 netfs_read_to_pagecache(rreq, NULL); ret =3D netfs_wait_for_read(rreq); + netfs_put_request(rreq, netfs_rreq_trace_put_return); if (ret < 0) goto error; - netfs_put_request(rreq, netfs_rreq_trace_put_return); =20 have_folio: ret =3D folio_wait_private_2_killable(folio); From nobody Thu May 7 05:50:19 2026 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 D0116480945 for ; Tue, 28 Apr 2026 13:19:41 +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=1777382383; cv=none; b=rD6QiHXFrfo1piGbLgGnrtfxYbekdvh26V1g3TRvh2IA9hnTMJHcFq17g3VPzeOMDpYHNDt61hfbF2Z2N1BjsqwjGMcff3ifdUdPjet8P5wKiwQ+ZEUU2Mp6IThqvZcMKqFOL3XbEfUcwnh+EHkdQZCScLTc0CwEGmYiOh+mcMk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382383; c=relaxed/simple; bh=MV8BsLrMSvER3VtBqtk6M+xC9GGfEG9YOInXw8Zjt00=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Z/T/elj+72qKUY1LsmO+nM4iQY5tV4Kr8PK6ZbsPPxauSL247c1/+ElZj77mUVMRBahqQKhZeWVx2R75ZZMNsd1qlIcs+pOjcym2Pa18fhvVhZsysqj03FSQEZtX2B89VG9K8/KEPDvfqcomcDy3Ohwulh+uyO1nthsMf0JhL2s= 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=d76wf011; 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="d76wf011" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382380; 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=igkxaROK0pbIaBuU08ythplTuPEJybILP1W7N8Z9Dh0=; b=d76wf0114PFPUwCvXkE8YlZWTLuy86ghKLKbgPNL295xHVeLvad71pQvevhEgwCX3fHGi9 Xj1B+pKMJGBmDwaUU4tt58RJRwQ028vdiD9Yzwwlpm4VVBcYc/HRYYTTjnM8IEQ89ey242 mLhOoW51wmcj8kDNqvNs3nS4B2hJmAs= 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-675-1n-4cWErO22kS_BHhqIFjQ-1; Tue, 28 Apr 2026 09:19:37 -0400 X-MC-Unique: 1n-4cWErO22kS_BHhqIFjQ-1 X-Mimecast-MFC-AGG-ID: 1n-4cWErO22kS_BHhqIFjQ_1777382374 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 6AFB219560BB; Tue, 28 Apr 2026 13:19:34 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3E37A19560AB; Tue, 28 Apr 2026 13:19:31 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Viacheslav Dubeyko , Matthew Wilcox Subject: [PATCH v5 19/24] netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages() Date: Tue, 28 Apr 2026 14:17:49 +0100 Message-ID: <20260428131756.922303-20-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" netfs_unlock_abandoned_read_pages(rreq) accesses the index of the folios it is wanting to unlock and compares that to rreq->no_unlock_folio so that it doesn't unlock a folio being read for netfs_perform_write() or netfs_write_begin(). However, given that netfs_unlock_abandoned_read_pages() is called _after_ NETFS_RREQ_IN_PROGRESS is cleared, the one folio that it's not allowed to dereference is the one specified by ->no_unlock_folio as ownership immediately reverts to the caller. Fix this by storing the folio pointer instead and using that rather than the index. Also fix netfs_unlock_read_folio() where the same applies. Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Viacheslav Dubeyko cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 4 ++-- fs/netfs/read_collect.c | 2 +- fs/netfs/read_retry.c | 2 +- include/linux/netfs.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index db279124d450..89ae5241c49c 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -670,7 +670,7 @@ int netfs_write_begin(struct netfs_inode *ctx, ret =3D PTR_ERR(rreq); goto error; } - rreq->no_unlock_folio =3D folio->index; + rreq->no_unlock_folio =3D folio; __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); =20 ret =3D netfs_begin_cache_read(rreq, ctx); @@ -736,7 +736,7 @@ int netfs_prefetch_for_write(struct file *file, struct = folio *folio, goto error; } =20 - rreq->no_unlock_folio =3D folio->index; + rreq->no_unlock_folio =3D folio; __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); ret =3D netfs_begin_cache_read(rreq, ctx); if (ret =3D=3D -ENOMEM || ret =3D=3D -EINTR || ret =3D=3D -ERESTARTSYS) diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index 3c9b847885c2..23660a590124 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -83,7 +83,7 @@ static void netfs_unlock_read_folio(struct netfs_io_reque= st *rreq, } =20 just_unlock: - if (folio->index =3D=3D rreq->no_unlock_folio && + if (folio =3D=3D rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { _debug("no unlock"); } else { diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index e10eb5a07332..f59a70f3a086 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -292,7 +292,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_= request *rreq) struct folio *folio =3D folioq_folio(p, slot); =20 if (folio && !folioq_is_marked2(p, slot)) { - if (folio->index =3D=3D rreq->no_unlock_folio && + if (folio =3D=3D rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { _debug("no unlock"); diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 1b1db8e6cdf1..9a68bee01c1a 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -252,7 +252,7 @@ struct netfs_io_request { unsigned long long collected_to; /* Point we've collected to */ unsigned long long cleaned_to; /* Position we've cleaned folios to */ unsigned long long abandon_to; /* Position to abandon folios to */ - pgoff_t no_unlock_folio; /* Don't unlock this folio after read */ + const struct folio *no_unlock_folio; /* Don't unlock this folio after rea= d */ unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ unsigned int debug_id; unsigned int rsize; /* Maximum read size (0 for none) */ From nobody Thu May 7 05:50:19 2026 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 D646D480972 for ; Tue, 28 Apr 2026 13:19:51 +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=1777382393; cv=none; b=r4F596YPdHWE0hIYrV6TZ5IZIRhLaUyqqwJ38vPKYu/iLf57i+khDIpNXX4hQ6rCTBsay0tDPTbIpPi4fQtRb17ZmoWWftliXbUoq/ECOng56pXgrjltdhW3jBUhVSyHFNo7V56UkH29rp00cANl+zcdXR62avz5jJD2KQwoY3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382393; c=relaxed/simple; bh=SrENox9QMrVI+/91R6omN9IfnXfzBhr5V2WP6gv3vV0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ignwVn6YGk5cGs9prMcsRjkaz/+1VpKbD5XMTCbO3BbSL+5UnuagBf1SyjPxIcZVoVvLsHurki/J1PN2WphyJySC8Pd7XE1vX5LlEWjnKeHigx6z+PNW3uGNaQknncnJA5dYIMOGICZ4aS/UBzXw0qaiDNazA4+C+W0mVMHSk04= 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=SIuG65BT; 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="SIuG65BT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382391; 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=zM9/5VMpPNkYtZBpulQv1ZMjXnFjfbGB90wD/QTCvo0=; b=SIuG65BTQzFQSIlPDS7nC0PzRHScTUuc1eCGhjaiBJpCxTQTTL/rm0ZQfJ0pt25H3HJvWh ILNV4X5M/m6JJKrQHNQO02hqLMaKnWYfFRDdg0jK6eKXFSNaO3fFaja+z9UOMNVU8MxAih ujkKwDwPsmg1LVK3Lw5zsQ/zbf4YFsY= 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-479-Hm7vc0ESPOWdLCKrRKKyHQ-1; Tue, 28 Apr 2026 09:19:45 -0400 X-MC-Unique: Hm7vc0ESPOWdLCKrRKKyHQ-1 X-Mimecast-MFC-AGG-ID: Hm7vc0ESPOWdLCKrRKKyHQ_1777382383 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 3821319774E1; Tue, 28 Apr 2026 13:19:39 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 063C1195608E; Tue, 28 Apr 2026 13:19:35 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 20/24] netfs: Fix partial invalidation of streaming-write folio Date: Tue, 28 Apr 2026 14:17:50 +0100 Message-ID: <20260428131756.922303-21-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" In netfs_invalidate_folio(), if the region of a partial invalidation overlaps the front (but not all) of a dirty write cached in a streaming write page (dirty, but not uptodate, with the dirty region tracked by a netfs_folio struct), the function modifies the dirty region - but incorrectly as it moves the region forward by setting the start to the start, not the end, of the invalidation region. Fix this by setting finfo->dirty_offset to the end of the invalidation region (iend). Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netf= s_inval_folio()") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 9fd41a97f83d..14565719b6a2 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -255,7 +255,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t= offset, size_t length) goto erase_completely; /* Move the start of the data. */ finfo->dirty_len =3D fend - iend; - finfo->dirty_offset =3D offset; + finfo->dirty_offset =3D iend; trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); return; } From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 95D00478859 for ; Tue, 28 Apr 2026 13:19:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382393; cv=none; b=eJlKrozZ4UnkhX3PDg0FYBVqXkJq8LVzxtl2YxjhmojRg6cYiMDUTjr2ieKL6lch/2YVF+mLTIVogQnk4ING3NXuNXQ0xokm2gKChhOKm+zZg57RVCzc5FoeajY1aD0XWp5dZ4ZZATTooQ5csBiDzHzmhEbNhCGFMggCqCfeFKc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382393; c=relaxed/simple; bh=oHKOxR6cBnJ7QN9TFzFAQLpYiRnGb+9i2FzlffHCgJo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RIwcW2bCcKo8sniBeL5OI+tVGyZyq7lJlkZYdC1wMnNNKNHQ0bONAGjjlKTDJOyrrDJWci5+3B9Zsda0V2YNBpp/6fgSnLbZ4gSwqk/tJTiwxVTIJDT27lIpmVf7wVsGC3Qm/QakpPv5ZrrL1OHv5YFCKsW2kcz+B/md64oD0T8= 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=UIqL9pat; arc=none smtp.client-ip=170.10.133.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="UIqL9pat" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382390; 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=WsvT1qZykLKhwF9QKeRj3MdcS5DVtfuhNsY2H0K61Go=; b=UIqL9patf5NCo79x27Anr9geZ85LSuL5nDyqxfa50K7EDKlpWtwxWfp2kaFZz78ho0ZXKr 3NaxTeQ6u71Gw+OxAGFEYd/jbT3iJKM0B14JsOYL5uEx9+5TO3i2nPj0xNiNZmYAQLDl5D FN3qrbrTfQ+alH/KAeZYr4p/ZEu29xg= 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-116-3q8oDHzfNESJUrV9iTspfQ-1; Tue, 28 Apr 2026 09:19:47 -0400 X-MC-Unique: 3q8oDHzfNESJUrV9iTspfQ-1 X-Mimecast-MFC-AGG-ID: 3q8oDHzfNESJUrV9iTspfQ_1777382386 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 B7F5B1944B0B; Tue, 28 Apr 2026 13:19:43 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CA3511800664; Tue, 28 Apr 2026 13:19:40 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 21/24] netfs: Fix folio->private handling in netfs_perform_write() Date: Tue, 28 Apr 2026 14:17:51 +0100 Message-ID: <20260428131756.922303-22-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Under some circumstances, netfs_perform_write() doesn't correctly manipulate folio->private between NULL, NETFS_FOLIO_COPY_TO_CACHE, pointing to a group and pointing to a netfs_folio struct, leading to potential multiple attachments of private data with associated folio ref leaks and also leaks of netfs_folio structs or netfs_group refs. Fix this by consolidating the place at which a folio is marked uptodate in one place and having that look at what's attached to folio->private and decide how to clean it up and then set the new group. Also, the content shouldn't be flushed if group is NULL, even if a group is specified in the netfs_group parameter, as that would be the case for a new folio. A filesystem should always specify netfs_group or never specify netfs_group. The Sashiko auto-review tool noted that it was theoretically possible that the fpos >=3D ctx->zero_point section might leak if it modified a streaming write folio. This is unlikely, but with a network filesystem, third party changes can happen. It also pointed out that __netfs_set_group() would leak if called multiple times on the same folio from the "whole folio modify section". Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs= _perform_write()") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_write.c | 113 ++++++++++++++++++++--------------- include/trace/events/netfs.h | 1 + 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 11a8f64b1177..1ae745c2c643 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -12,24 +12,6 @@ #include #include "internal.h" =20 -static void __netfs_set_group(struct folio *folio, struct netfs_group *net= fs_group) -{ - if (netfs_group) - folio_attach_private(folio, netfs_get_group(netfs_group)); -} - -static void netfs_set_group(struct folio *folio, struct netfs_group *netfs= _group) -{ - void *priv =3D folio_get_private(folio); - - if (unlikely(priv !=3D netfs_group)) { - if (netfs_group && (!priv || priv =3D=3D NETFS_FOLIO_COPY_TO_CACHE)) - folio_attach_private(folio, netfs_get_group(netfs_group)); - else if (!netfs_group && priv =3D=3D NETFS_FOLIO_COPY_TO_CACHE) - folio_detach_private(folio); - } -} - /* * Grab a folio for writing and lock it. Attempt to allocate as large a f= olio * as possible to hold as much of the remaining length as possible in one = go. @@ -157,6 +139,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, size_t offset; /* Offset into pagecache folio */ size_t part; /* Bytes to write to folio */ size_t copied; /* Bytes copied from user */ + void *priv; =20 offset =3D pos & (max_chunk - 1); part =3D min(max_chunk - offset, iov_iter_count(iter)); @@ -212,8 +195,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, finfo =3D netfs_folio_info(folio); group =3D netfs_folio_group(folio); =20 - if (unlikely(group !=3D netfs_group) && - group !=3D NETFS_FOLIO_COPY_TO_CACHE) + if (unlikely(group) && + group !=3D NETFS_FOLIO_COPY_TO_CACHE && + group !=3D netfs_group) goto flush_content; =20 if (folio_test_uptodate(folio)) { @@ -222,9 +206,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, copied =3D copy_folio_from_iter_atomic(folio, offset, part, iter); if (unlikely(copied =3D=3D 0)) goto copy_failed; - netfs_set_group(folio, netfs_group); trace =3D netfs_folio_is_uptodate; - goto copied; + goto copied_uptodate; } =20 /* If the page is above the zero-point then we assume that the @@ -237,24 +220,22 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struc= t iov_iter *iter, if (unlikely(copied =3D=3D 0)) goto copy_failed; folio_zero_segment(folio, offset + copied, flen); - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); - trace =3D netfs_modify_and_clear; - goto copied; + if (finfo) + trace =3D netfs_modify_and_clear_rm_finfo; + else + trace =3D netfs_modify_and_clear; + goto mark_uptodate; } =20 /* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset =3D=3D 0 && part >=3D flen) { copied =3D copy_folio_from_iter_atomic(folio, offset, part, iter); if (likely(copied =3D=3D part)) { - if (finfo) { + if (finfo) trace =3D netfs_whole_folio_modify_filled; - goto folio_now_filled; - } - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); - trace =3D netfs_whole_folio_modify; - goto copied; + else + trace =3D netfs_whole_folio_modify; + goto mark_uptodate; } if (copied =3D=3D 0) goto copy_failed; @@ -272,7 +253,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, finfo->dirty_len +=3D finfo->dirty_offset; if (finfo->dirty_len =3D=3D flen) { trace =3D netfs_whole_folio_modify_filled_efault; - goto folio_now_filled; + goto mark_uptodate; } if (copied > finfo->dirty_len) finfo->dirty_len =3D copied; @@ -300,11 +281,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struc= t iov_iter *iter, copied =3D copy_folio_from_iter_atomic(folio, offset, part, iter); if (unlikely(copied =3D=3D 0)) goto copy_failed; - netfs_set_group(folio, netfs_group); trace =3D netfs_just_prefetch; - goto copied; + goto copied_uptodate; } =20 + /* Do a streaming write on a folio that has nothing in it yet. */ if (!finfo) { ret =3D -EIO; if (WARN_ON(folio_get_private(folio))) @@ -313,10 +294,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct= iov_iter *iter, if (unlikely(copied =3D=3D 0)) goto copy_failed; if (offset =3D=3D 0 && copied =3D=3D flen) { - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); trace =3D netfs_streaming_filled_page; - goto copied; + goto mark_uptodate; } =20 finfo =3D kzalloc_obj(*finfo); @@ -345,7 +324,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct = iov_iter *iter, finfo->dirty_len +=3D copied; if (finfo->dirty_offset =3D=3D 0 && finfo->dirty_len =3D=3D flen) { trace =3D netfs_streaming_cont_filled_page; - goto folio_now_filled; + goto mark_uptodate; } trace =3D netfs_streaming_write_cont; goto copied; @@ -361,13 +340,36 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struc= t iov_iter *iter, goto out; continue; =20 - folio_now_filled: - if (finfo->netfs_group) - folio_change_private(folio, finfo->netfs_group); - else - folio_detach_private(folio); + /* Mark a folio as being up to data when we've filled it + * completely. If the folio has a group attached, then it must + * be the same group, otherwise we should have flushed it out + * above. We have to get rid of the netfs_folio struct if + * there was one. + */ + mark_uptodate: folio_mark_uptodate(folio); - kfree(finfo); + + copied_uptodate: + priv =3D folio_get_private(folio); + if (likely(priv =3D=3D netfs_group)) { + /* Already set correctly; no change required. */ + } else if (priv =3D=3D NETFS_FOLIO_COPY_TO_CACHE) { + if (!netfs_group) + folio_detach_private(folio); + else + folio_change_private(folio, netfs_get_group(netfs_group)); + } else if (!priv) { + folio_attach_private(folio, netfs_get_group(netfs_group)); + } else { + WARN_ON_ONCE(!finfo); + if (netfs_group) + /* finfo->netfs_group has a ref */ + folio_change_private(folio, netfs_group); + else + folio_detach_private(folio); + kfree(finfo); + } + copied: trace_netfs_folio(folio, trace); flush_dcache_folio(folio); @@ -530,6 +532,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, str= uct netfs_group *netfs_gr struct inode *inode =3D file_inode(file); struct netfs_inode *ictx =3D netfs_inode(inode); vm_fault_t ret =3D VM_FAULT_NOPAGE; + void *priv; int err; =20 _enter("%lx", folio->index); @@ -550,7 +553,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, str= uct netfs_group *netfs_gr } =20 group =3D netfs_folio_group(folio); - if (group !=3D netfs_group && group !=3D NETFS_FOLIO_COPY_TO_CACHE) { + if (group && + group !=3D netfs_group && + group !=3D NETFS_FOLIO_COPY_TO_CACHE) { folio_unlock(folio); err =3D filemap_fdatawrite_range(mapping, folio_pos(folio), @@ -572,7 +577,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, st= ruct netfs_group *netfs_gr trace_netfs_folio(folio, netfs_folio_trace_mkwrite_plus); else trace_netfs_folio(folio, netfs_folio_trace_mkwrite); - netfs_set_group(folio, netfs_group); + + priv =3D folio_get_private(folio); + if (priv !=3D netfs_group) { + if (!netfs_group && priv =3D=3D NETFS_FOLIO_COPY_TO_CACHE) + folio_detach_private(folio); + else if (netfs_group && priv =3D=3D NETFS_FOLIO_COPY_TO_CACHE) + folio_change_private(folio, netfs_get_group(netfs_group)); + else if (netfs_group && !priv) + folio_attach_private(folio, netfs_get_group(netfs_group)); + else + WARN_ON_ONCE(1); + } + file_update_time(file); set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags); if (ictx->ops->post_modify) diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index aa9940ba307b..082cb03c6131 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -181,6 +181,7 @@ EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ + EM(netfs_modify_and_clear_rm_finfo, "mod-n-clear+") \ EM(netfs_streaming_write, "mod-streamw") \ EM(netfs_streaming_write_cont, "mod-streamw+") \ EM(netfs_flush_content, "flush") \ From nobody Thu May 7 05:50:19 2026 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 4FA7E44B68D for ; Tue, 28 Apr 2026 13:20:17 +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=1777382418; cv=none; b=oew02srSWLr32Jk2p3Pc1DLNGdznhWvyIW6oAPjx/IfAzBT2grlUzX68c1rEGnWQNo9Uyw2RO9Y8uyt7C08XKGSTZHPaaz4/iuzWVzbHetwvvUrov9xpFEDcLDia1cLbV2E7lfrbxSIvxPGUQ7y3cOswKh43vLQMUR4LJJn/x9o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382418; c=relaxed/simple; bh=ew53F1uP88FPC5BpDNF4Nw8Efi7JfTtT1jRQAGT1loI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=V+ZoULo7ls0sIuBfUR/JrhxLa69cEkcisAhU0CxbWUQK5a1IpY//X8jm3LSQi++x3PrOtXBU90VW0CLVEp2sZioffAwNiokpgCfLC5/6IammWjg+0JWX+GRb1kHGBO7wLNIhtEJVol5VIpBb/OkAfZMaEdx3vVlaugVaiTJ47WY= 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=WJnmzDvw; 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="WJnmzDvw" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382416; 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=Ip+PNQg7TgqHbl0SFCzUvei4CjuFE2MM4RySvRK0koo=; b=WJnmzDvwxD4er+8jQ4f4Y4ykahrIJIlXl3tZoBsGpGjNXxpUDYytGYTqcLKLgEteFxH0Cu TXe4HefWyZBTqqNe1B71hZ3X1SYQcaxSA171ODGLjex0VCHZzzj2JgprM5j/DtBgaPK/xv YoOM5F69W86Cfo74MRJ8qssxCrpoGWE= 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-107-6sj-aSdDPhW1sbsvXgcBtg-1; Tue, 28 Apr 2026 09:20:13 -0400 X-MC-Unique: 6sj-aSdDPhW1sbsvXgcBtg-1 X-Mimecast-MFC-AGG-ID: 6sj-aSdDPhW1sbsvXgcBtg_1777382411 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 62684187D1AA; Tue, 28 Apr 2026 13:19:48 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6A327196B8FC; Tue, 28 Apr 2026 13:19:45 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matthew Wilcox Subject: [PATCH v5 22/24] netfs: Fix netfs_read_folio() to wait on writeback Date: Tue, 28 Apr 2026 14:17:52 +0100 Message-ID: <20260428131756.922303-23-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Fix netfs_read_folio() to wait for an ongoing writeback to complete so that it can trust the dirty flag and whatever is attached to folio->private (folio->private may get cleaned up by the collector before it clears the writeback flag). Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40= redhat.com Signed-off-by: David Howells cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org --- fs/netfs/buffered_read.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 89ae5241c49c..1333f0b71985 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -503,6 +503,8 @@ int netfs_read_folio(struct file *file, struct folio *f= olio) struct netfs_inode *ctx =3D netfs_inode(mapping->host); int ret; =20 + folio_wait_writeback(folio); + if (folio_test_dirty(folio)) return netfs_read_gaps(file, folio); From nobody Thu May 7 05:50:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 BF4DE48124A for ; Tue, 28 Apr 2026 13:20:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382402; cv=none; b=ECoed0AbbC6v30OUYjop7iiF39uEVNJI/hZwEUMc9BLCzoN/7pDaRIPbRUG/Aj9HgfzAy41zxYQR3brk0bvsTVjF0R/RW/rQjM7YbTrjmgTSvuOoC1a+/6spREQdZJqSuG4ElvKiBhmkpc1oW98x2RqXUP5EW0N5WSI/hyv96G4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382402; c=relaxed/simple; bh=hhlZHzVkZGgNw65TAqSXoZn8aY9+h0Opjd6qveHBrl8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AWnkgwCy0OXUh0f6vfvj82a7e0Qj+enOznIl+8uEYvwB1T1x0MKFZeOEGQiNcAeuAeMV1PiLCLMYgSvh9wQCgKGqj4D0Be9kQrriRzJhDMmlqbtlvfnzWbeu0mEO/ipl1u8cD8eZiKkL8/NaeaUoOZmZCCQhIGHcZNS38Yh65Ks= 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=fQX5rp4b; arc=none smtp.client-ip=170.10.133.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="fQX5rp4b" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382400; 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=mt629XDWKB+tZOOSAJE2LdFWKC9Q9poIqsffgDORXAw=; b=fQX5rp4b4y//9xNTLe4kit+WBaYQ5aHWbVWGBHoqwAGMSYND+vg63xYkuxw4Q60HYfvjpQ /MvXva9NPw+uJiPkEGxkVMyv9wl7o/4DvXupiSFesAfoCcBnIlD7wcDSTuIQGWhs6hfy70 BwW9VKsjFvTNvnCvowfZ4mX1zdT5krs= 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-474-3o6WZebRMVq1RarP7C2BZg-1; Tue, 28 Apr 2026 09:19:54 -0400 X-MC-Unique: 3o6WZebRMVq1RarP7C2BZg-1 X-Mimecast-MFC-AGG-ID: 3o6WZebRMVq1RarP7C2BZg_1777382393 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 03053195605B; Tue, 28 Apr 2026 13:19:53 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1473F180047F; Tue, 28 Apr 2026 13:19:49 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Marc Dionne Subject: [PATCH v5 23/24] netfs, afs: Fix write skipping in dir/link writepages Date: Tue, 28 Apr 2026 14:17:53 +0100 Message-ID: <20260428131756.922303-24-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Fix netfs_write_single() and afs_single_writepages() to better handle a write that would be skipped due to lock contention and WB_SYNC_NONE by returning 1 from netfs_write_single() if it skipped and making afs_single_writepages() skip also. If a skip occurs, the inode must be re-marked as the VFS may have cleared the mark. This is really only theoretical for directories in netfs_write_single() as the only path to that is through afs_single_writepages() that takes the ->validate_lock around it, thereby serialising it. Fixes: 6dd80936618c ("afs: Use netfslib for directories") Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org --- fs/afs/dir.c | 11 ++++++++++- fs/netfs/write_issue.c | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index aaaa55878ffd..d1542a1a50bf 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -2206,7 +2206,14 @@ int afs_single_writepages(struct address_space *mapp= ing, /* Need to lock to prevent the folio queue and folios from being thrown * away. */ - down_read(&dvnode->validate_lock); + if (!down_read_trylock(&dvnode->validate_lock)) { + if (wbc->sync_mode =3D=3D WB_SYNC_NONE) { + /* The VFS will have undirtied the inode. */ + netfs_single_mark_inode_dirty(&dvnode->netfs.inode); + return 0; + } + down_read(&dvnode->validate_lock); + } =20 if (is_dir ? test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : @@ -2214,6 +2221,8 @@ int afs_single_writepages(struct address_space *mappi= ng, iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0, i_size_read(&dvnode->netfs.inode)); ret =3D netfs_writeback_single(mapping, wbc, &iter); + if (ret =3D=3D 1) + ret =3D 0; /* Skipped write due to lock conflict. */ } =20 up_read(&dvnode->validate_lock); diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 0b07ebecb157..a85ec300b79a 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -823,6 +823,9 @@ static int netfs_write_folio_single(struct netfs_io_req= uest *wreq, * * Write a monolithic, non-pagecache object back to the server and/or * the cache. + * + * Return: 0 if successful; 1 if skipped due to lock conflict and WB_SYNC_= NONE; + * or a negative error code. */ int netfs_writeback_single(struct address_space *mapping, struct writeback_control *wbc, @@ -839,8 +842,10 @@ int netfs_writeback_single(struct address_space *mappi= ng, =20 if (!mutex_trylock(&ictx->wb_lock)) { if (wbc->sync_mode =3D=3D WB_SYNC_NONE) { + /* The VFS will have undirtied the inode. */ + netfs_single_mark_inode_dirty(&ictx->inode); netfs_stat(&netfs_n_wb_lock_skip); - return 0; + return 1; } netfs_stat(&netfs_n_wb_lock_wait); mutex_lock(&ictx->wb_lock); From nobody Thu May 7 05:50:19 2026 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 4AF63466B58 for ; Tue, 28 Apr 2026 13:20:22 +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=1777382424; cv=none; b=FsaFaOMYm8/nsLE9DH1WyRWl7zV2nBo88QtkIvoPqPx9+Q/QzcUUcfTju5ZthcLDHYBCJd9m9//gQPBURsV25CUUMfy2uOgj78io/ZGQdrJJNxSJwFMBNNo8jkFWwP020ysIiUx8bbSrurEhRpzOeJvWhGcZOXu2999KTNei+28= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777382424; c=relaxed/simple; bh=a6zHwwgsg5eF6pvdUJ9DmiNTCHXJZM5+9vpSX+f+OUc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OoUItAMl4tprU5pXhb0YYHELK2m6e3zlLoTnc8swA6J/wkoHbfVND8UAHTHEDkN2ZFMx1o1PvtjrQVeN1neHcGBJljdUDq2shuNQXlGW0t5o515VpcTjcI2Wt7xo+17BYCzVlD1hd9Nb7nD3ON8B/mP901Q28OySPOiJaRVCwE0= 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=DMm03QxK; 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="DMm03QxK" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777382421; 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=UoRl1kjJpjXytSNjoTWlxcU9fGMWXmOzrKUK0nBr27A=; b=DMm03QxKx4nuxJpCl50rBSlT7YP3oDwbpXwXwFOHQvRFk7xvqfvf0ZO0pIAR6Fr09swNAQ YXS3I272f73nOzMop1iti3f77mZXi84IVy7vpKdmkO1zcY+8yRlxvdsdtQmqVGjhxblkha V1ygadqbpe76XH+XU65WBeCkG4+ct9I= 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-193-9DYd25c5NWObskavSl18ag-1; Tue, 28 Apr 2026 09:20:16 -0400 X-MC-Unique: 9DYd25c5NWObskavSl18ag-1 X-Mimecast-MFC-AGG-ID: 9DYd25c5NWObskavSl18ag_1777382414 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 B1C871869DB6; Tue, 28 Apr 2026 13:19:57 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.44.32.126]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id AC10319560AB; Tue, 28 Apr 2026 13:19:54 +0000 (UTC) From: David Howells To: Christian Brauner Cc: David Howells , Paulo Alcantara , netfs@lists.linux.dev, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Marc Dionne Subject: [PATCH v5 24/24] afs: Fix the locking used by afs_get_link() Date: Tue, 28 Apr 2026 14:17:54 +0100 Message-ID: <20260428131756.922303-25-dhowells@redhat.com> In-Reply-To: <20260428131756.922303-1-dhowells@redhat.com> References: <20260428131756.922303-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" The afs filesystem in the kernel doesn't do locking correctly for symbolic links. There are a number of problems: (1) It doesn't do any locking around afs_read_single() to prevent races between multiple ->get_link() calls, thereby allowing the possibility of leaks. (2) It doesn't use RCU barriering when accessing the buffer pointers during RCU pathwalk. (3) It can race with another thread updating the contents of the symlink if a third party updated it on the server. Fix this by the following means: (0) Move symlink handling into its own file as this makes it more complicated. (1) Take the validate_lock around afs_read_single() to prevent races between multiple ->get_link() calls. (2) Keep a separate copy of the symlink contents with an rcu_head. This is always going to be a lot smaller than a page, so it can be kmalloc'd and save quite a bit of memory. It also needs a refcount for non-RCU pathwalk. (3) Split the symlink read and write-to-cache routines in afs from those for directories. (4) Discard the I/O buffer as soon as the write-to-cache completes as this is a full page (plus a folio_queue). (5) If there's no cache, discard the I/O buffer immediately after reading and copying if there is no cache. Fixes: eae9e78951bb ("afs: Use netfslib for symlinks, allowing them to be c= ached") Fixes: 6698c02d64b2 ("afs: Locally initialise the contents of a new symlink= on creation") Closes: https://sashiko.dev/#/patchset/20260326104544.509518-1-dhowells%40r= edhat.com Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org --- fs/afs/Makefile | 1 + fs/afs/dir.c | 68 +++++------ fs/afs/fsclient.c | 4 +- fs/afs/inode.c | 96 +--------------- fs/afs/internal.h | 34 ++++-- fs/afs/symlink.c | 270 ++++++++++++++++++++++++++++++++++++++++++++ fs/afs/validation.c | 8 +- fs/afs/yfsclient.c | 4 +- 8 files changed, 345 insertions(+), 140 deletions(-) create mode 100644 fs/afs/symlink.c diff --git a/fs/afs/Makefile b/fs/afs/Makefile index b49b8fe682f3..0d8f1982d596 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -30,6 +30,7 @@ kafs-y :=3D \ server.o \ server_list.o \ super.o \ + symlink.o \ validation.o \ vlclient.o \ vl_alias.o \ diff --git a/fs/afs/dir.c b/fs/afs/dir.c index d1542a1a50bf..498b99ccdf0e 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -44,6 +44,8 @@ static int afs_symlink(struct mnt_idmap *idmap, struct in= ode *dir, static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); +static int afs_dir_writepages(struct address_space *mapping, + struct writeback_control *wbc); =20 const struct file_operations afs_dir_file_operations =3D { .open =3D afs_dir_open, @@ -68,7 +70,7 @@ const struct inode_operations afs_dir_inode_operations = =3D { }; =20 const struct address_space_operations afs_dir_aops =3D { - .writepages =3D afs_single_writepages, + .writepages =3D afs_dir_writepages, }; =20 const struct dentry_operations afs_fs_dentry_operations =3D { @@ -233,22 +235,13 @@ static ssize_t afs_do_read_single(struct afs_vnode *d= vnode, struct file *file) struct iov_iter iter; ssize_t ret; loff_t i_size; - bool is_dir =3D (S_ISDIR(dvnode->netfs.inode.i_mode) && - !test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags)); =20 i_size =3D i_size_read(&dvnode->netfs.inode); - if (is_dir) { - if (i_size < AFS_DIR_BLOCK_SIZE) - return afs_bad(dvnode, afs_file_error_dir_small); - if (i_size > AFS_DIR_BLOCK_SIZE * 1024) { - trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); - return -EFBIG; - } - } else { - if (i_size > AFSPATHMAX) { - trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); - return -EFBIG; - } + if (i_size < AFS_DIR_BLOCK_SIZE) + return afs_bad(dvnode, afs_file_error_dir_small); + if (i_size > AFS_DIR_BLOCK_SIZE * 1024) { + trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); + return -EFBIG; } =20 /* Expand the storage. TODO: Shrink the storage too. */ @@ -277,24 +270,18 @@ static ssize_t afs_do_read_single(struct afs_vnode *d= vnode, struct file *file) * buffer. */ ret =3D -ESTALE; - } else if (is_dir) { + } else { int ret2 =3D afs_dir_check(dvnode); =20 if (ret2 < 0) ret =3D ret2; - } else if (i_size < folioq_folio_size(dvnode->directory, 0)) { - /* NUL-terminate a symlink. */ - char *symlink =3D kmap_local_folio(folioq_folio(dvnode->directory, 0), = 0); - - symlink[i_size] =3D 0; - kunmap_local(symlink); } } =20 return ret; } =20 -ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file) +static ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file) { ssize_t ret; =20 @@ -1763,13 +1750,20 @@ static int afs_link(struct dentry *from, struct ino= de *dir, return ret; } =20 +static void afs_symlink_put(struct afs_operation *op) +{ + kfree(op->create.symlink); + op->create.symlink =3D NULL; + afs_create_put(op); +} + static const struct afs_operation_ops afs_symlink_operation =3D { .issue_afs_rpc =3D afs_fs_symlink, .issue_yfs_rpc =3D yfs_fs_symlink, .success =3D afs_create_success, .aborted =3D afs_check_for_remote_deletion, .edit_dir =3D afs_create_edit_dir, - .put =3D afs_create_put, + .put =3D afs_symlink_put, }; =20 /* @@ -1779,7 +1773,9 @@ static int afs_symlink(struct mnt_idmap *idmap, struc= t inode *dir, struct dentry *dentry, const char *content) { struct afs_operation *op; + struct afs_symlink *symlink; struct afs_vnode *dvnode =3D AFS_FS_I(dir); + size_t clen =3D strlen(content); int ret; =20 _enter("{%llx:%llu},{%pd},%s", @@ -1791,12 +1787,20 @@ static int afs_symlink(struct mnt_idmap *idmap, str= uct inode *dir, goto error; =20 ret =3D -EINVAL; - if (strlen(content) >=3D AFSPATHMAX) + if (clen >=3D AFSPATHMAX) + goto error; + + ret =3D -ENOMEM; + symlink =3D kmalloc_flex(struct afs_symlink, content, clen + 1, GFP_KERNE= L); + if (!symlink) goto error; + refcount_set(&symlink->ref, 1); + memcpy(symlink->content, content, clen + 1); =20 op =3D afs_alloc_operation(NULL, dvnode->volume); if (IS_ERR(op)) { ret =3D PTR_ERR(op); + kfree(symlink); goto error; } =20 @@ -1808,7 +1812,7 @@ static int afs_symlink(struct mnt_idmap *idmap, struc= t inode *dir, op->dentry =3D dentry; op->ops =3D &afs_symlink_operation; op->create.reason =3D afs_edit_dir_for_symlink; - op->create.symlink =3D content; + op->create.symlink =3D symlink; op->mtime =3D current_time(dir); ret =3D afs_do_sync_operation(op); afs_dir_unuse_cookie(dvnode, ret); @@ -2192,15 +2196,13 @@ static int afs_rename(struct mnt_idmap *idmap, stru= ct inode *old_dir, } =20 /* - * Write the file contents to the cache as a single blob. + * Write the directory contents to the cache as a single blob. */ -int afs_single_writepages(struct address_space *mapping, - struct writeback_control *wbc) +static int afs_dir_writepages(struct address_space *mapping, + struct writeback_control *wbc) { struct afs_vnode *dvnode =3D AFS_FS_I(mapping->host); struct iov_iter iter; - bool is_dir =3D (S_ISDIR(dvnode->netfs.inode.i_mode) && - !test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags)); int ret =3D 0; =20 /* Need to lock to prevent the folio queue and folios from being thrown @@ -2215,9 +2217,7 @@ int afs_single_writepages(struct address_space *mappi= ng, down_read(&dvnode->validate_lock); } =20 - if (is_dir ? - test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : - atomic64_read(&dvnode->cb_expires_at) !=3D AFS_NO_CB_PROMISE) { + if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0, i_size_read(&dvnode->netfs.inode)); ret =3D netfs_writeback_single(mapping, wbc, &iter); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 95494d5f2b8a..a2ffd60889f8 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -886,7 +886,7 @@ void afs_fs_symlink(struct afs_operation *op) namesz =3D name->len; padsz =3D (4 - (namesz & 3)) & 3; =20 - c_namesz =3D strlen(op->create.symlink); + c_namesz =3D strlen(op->create.symlink->content); c_padsz =3D (4 - (c_namesz & 3)) & 3; =20 reqsz =3D (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4); @@ -910,7 +910,7 @@ void afs_fs_symlink(struct afs_operation *op) bp =3D (void *) bp + padsz; } *bp++ =3D htonl(c_namesz); - memcpy(bp, op->create.symlink, c_namesz); + memcpy(bp, op->create.symlink->content, c_namesz); bp =3D (void *) bp + c_namesz; if (c_padsz > 0) { memset(bp, 0, c_padsz); diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 06e25e1b12df..e8b0c5cec5d6 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -25,96 +25,6 @@ #include "internal.h" #include "afs_fs.h" =20 -void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *o= p) -{ - size_t size =3D strlen(op->create.symlink) + 1; - size_t dsize =3D 0; - char *p; - - if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size, - mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0) - return; - - vnode->directory_size =3D dsize; - p =3D kmap_local_folio(folioq_folio(vnode->directory, 0), 0); - memcpy(p, op->create.symlink, size); - kunmap_local(p); - set_bit(AFS_VNODE_DIR_READ, &vnode->flags); - netfs_single_mark_inode_dirty(&vnode->netfs.inode); -} - -static void afs_put_link(void *arg) -{ - struct folio *folio =3D virt_to_folio(arg); - - kunmap_local(arg); - folio_put(folio); -} - -const char *afs_get_link(struct dentry *dentry, struct inode *inode, - struct delayed_call *callback) -{ - struct afs_vnode *vnode =3D AFS_FS_I(inode); - struct folio *folio; - char *content; - ssize_t ret; - - if (!dentry) { - /* RCU pathwalk. */ - if (!test_bit(AFS_VNODE_DIR_READ, &vnode->flags) || !afs_check_validity(= vnode)) - return ERR_PTR(-ECHILD); - goto good; - } - - if (test_bit(AFS_VNODE_DIR_READ, &vnode->flags)) - goto fetch; - - ret =3D afs_validate(vnode, NULL); - if (ret < 0) - return ERR_PTR(ret); - - if (!test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && - test_bit(AFS_VNODE_DIR_READ, &vnode->flags)) - goto good; - -fetch: - ret =3D afs_read_single(vnode, NULL); - if (ret < 0) - return ERR_PTR(ret); - set_bit(AFS_VNODE_DIR_READ, &vnode->flags); - -good: - folio =3D folioq_folio(vnode->directory, 0); - folio_get(folio); - content =3D kmap_local_folio(folio, 0); - set_delayed_call(callback, afs_put_link, content); - return content; -} - -int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen) -{ - DEFINE_DELAYED_CALL(done); - const char *content; - int len; - - content =3D afs_get_link(dentry, d_inode(dentry), &done); - if (IS_ERR(content)) { - do_delayed_call(&done); - return PTR_ERR(content); - } - - len =3D umin(strlen(content), buflen); - if (copy_to_user(buffer, content, len)) - len =3D -EFAULT; - do_delayed_call(&done); - return len; -} - -static const struct inode_operations afs_symlink_inode_operations =3D { - .get_link =3D afs_get_link, - .readlink =3D afs_readlink, -}; - static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode = *parent_vnode) { static unsigned long once_only; @@ -214,7 +124,7 @@ static int afs_inode_init_from_status(struct afs_operat= ion *op, inode->i_mode =3D S_IFLNK | status->mode; inode->i_op =3D &afs_symlink_inode_operations; } - inode->i_mapping->a_ops =3D &afs_dir_aops; + inode->i_mapping->a_ops =3D &afs_symlink_aops; inode_nohighmem(inode); mapping_set_release_always(inode->i_mapping); break; @@ -756,12 +666,14 @@ void afs_evict_inode(struct inode *inode) .range_end =3D LLONG_MAX, }; =20 - afs_single_writepages(inode->i_mapping, &wbc); + inode->i_mapping->a_ops->writepages(inode->i_mapping, &wbc); } =20 netfs_wait_for_outstanding_io(inode); truncate_inode_pages_final(&inode->i_data); netfs_free_folioq_buffer(vnode->directory); + if (vnode->symlink) + afs_evict_symlink(vnode); =20 afs_set_cache_aux(vnode, &aux); netfs_clear_inode_writeback(inode, &aux); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 599353c33337..f7502ee90016 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -710,6 +710,7 @@ struct afs_vnode { #define AFS_VNODE_DIR_READ 11 /* Set if we've read a dir's contents */ =20 struct folio_queue *directory; /* Directory contents */ + struct afs_symlink __rcu *symlink; /* Symlink content */ struct list_head wb_keys; /* List of keys available for writeback */ struct list_head pending_locks; /* locks waiting to be granted */ struct list_head granted_locks; /* locks granted on this file */ @@ -776,6 +777,15 @@ struct afs_permits { struct afs_permit permits[] __counted_by(nr_permits); /* List of permits = sorted by key pointer */ }; =20 +/* + * Copy of symlink content for normal use. + */ +struct afs_symlink { + struct rcu_head rcu; + refcount_t ref; + char content[]; +}; + /* * Error prioritisation and accumulation. */ @@ -887,7 +897,7 @@ struct afs_operation { struct { int reason; /* enum afs_edit_dir_reason */ mode_t mode; - const char *symlink; + struct afs_symlink *symlink; } create; struct { bool need_rehash; @@ -1098,13 +1108,10 @@ extern const struct inode_operations afs_dir_inode_= operations; extern const struct address_space_operations afs_dir_aops; extern const struct dentry_operations afs_fs_dentry_operations; =20 -ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file); ssize_t afs_read_dir(struct afs_vnode *dvnode, struct file *file) __acquires(&dvnode->validate_lock); extern void afs_d_release(struct dentry *); extern void afs_check_for_remote_deletion(struct afs_operation *); -int afs_single_writepages(struct address_space *mapping, - struct writeback_control *wbc); =20 /* * dir_edit.c @@ -1246,10 +1253,6 @@ extern void afs_fs_probe_cleanup(struct afs_net *); */ extern const struct afs_operation_ops afs_fetch_status_operation; =20 -void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *o= p); -const char *afs_get_link(struct dentry *dentry, struct inode *inode, - struct delayed_call *callback); -int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen); extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vno= de_param *); extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_ac= cess_t *); extern int afs_ilookup5_test_by_fid(struct inode *, void *); @@ -1599,6 +1602,21 @@ void afs_detach_volume_from_servers(struct afs_volum= e *volume, struct afs_server extern int __init afs_fs_init(void); extern void afs_fs_exit(void); =20 +/* + * symlink.c + */ +extern const struct inode_operations afs_symlink_inode_operations; +extern const struct address_space_operations afs_symlink_aops; + +void afs_invalidate_symlink(struct afs_vnode *vnode); +void afs_evict_symlink(struct afs_vnode *vnode); +void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *o= p); +const char *afs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *callback); +int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen); +int afs_symlink_writepages(struct address_space *mapping, + struct writeback_control *wbc); + /* * validation.c */ diff --git a/fs/afs/symlink.c b/fs/afs/symlink.c new file mode 100644 index 000000000000..7a67875d8d8c --- /dev/null +++ b/fs/afs/symlink.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* AFS filesystem symbolic link handling + * + * Copyright (C) 2026 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include +#include "internal.h" + +static void afs_put_symlink(struct afs_symlink *symlink) +{ + if (refcount_dec_and_test(&symlink->ref)) + kfree_rcu(symlink, rcu); +} + +static void afs_replace_symlink(struct afs_vnode *vnode, struct afs_symlin= k *symlink) +{ + struct afs_symlink *old; + + old =3D rcu_replace_pointer(vnode->symlink, symlink, + lockdep_is_held(&vnode->validate_lock)); + if (old) + afs_put_symlink(old); +} + +/* + * In the event that a third-party update of a symlink occurs, dispose of = the + * copy of the old contents. Called under ->validate_lock. + */ +void afs_invalidate_symlink(struct afs_vnode *vnode) +{ + afs_replace_symlink(vnode, NULL); +} + +/* + * Dispose of a symlink copy during inode deletion. + */ +void afs_evict_symlink(struct afs_vnode *vnode) +{ + afs_invalidate_symlink(vnode); +} + +/* + * Set up a locally created symlink inode for immediate write to the cache. + */ +void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *o= p) +{ + size_t dsize =3D 0; + size_t size =3D strlen(op->create.symlink->content) + 1; + char *p; + + rcu_assign_pointer(vnode->symlink, op->create.symlink); + op->create.symlink =3D NULL; + + if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs))) + return; + + if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size, + mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0) + return; + + vnode->directory_size =3D dsize; + p =3D kmap_local_folio(folioq_folio(vnode->directory, 0), 0); + memcpy(p, vnode->symlink->content, size); + kunmap_local(p); + netfs_single_mark_inode_dirty(&vnode->netfs.inode); +} + +/* + * Read a symlink in a single download. + */ +static ssize_t afs_do_read_symlink(struct afs_vnode *vnode) +{ + struct afs_symlink *symlink; + struct iov_iter iter; + ssize_t ret; + loff_t i_size; + + i_size =3D i_size_read(&vnode->netfs.inode); + if (i_size > PAGE_SIZE - 1) { + trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big); + return -EFBIG; + } + + if (!vnode->directory) { + size_t cur_size =3D 0; + + ret =3D netfs_alloc_folioq_buffer(NULL, + &vnode->directory, &cur_size, PAGE_SIZE, + mapping_gfp_mask(vnode->netfs.inode.i_mapping)); + vnode->directory_size =3D PAGE_SIZE - 1; + if (ret < 0) + return ret; + } + + iov_iter_folio_queue(&iter, ITER_DEST, vnode->directory, 0, 0, PAGE_SIZE); + + /* AFS requires us to perform the read of a symlink as a single unit to + * avoid issues with the content being changed between reads. + */ + ret =3D netfs_read_single(&vnode->netfs.inode, NULL, &iter); + if (ret >=3D 0) { + i_size =3D i_size_read(&vnode->netfs.inode); + if (i_size > PAGE_SIZE - 1) { + trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big); + return -EFBIG; + } + vnode->directory_size =3D i_size; + + /* Copy the symlink. */ + symlink =3D kmalloc_flex(struct afs_symlink, content, i_size + 1, + GFP_KERNEL); + if (!symlink) + return -ENOMEM; + + refcount_set(&symlink->ref, 1); + symlink->content[i_size] =3D 0; + + const char *s =3D kmap_local_folio(folioq_folio(vnode->directory, 0), 0); + + memcpy(symlink->content, s, i_size); + kunmap_local(s); + + afs_replace_symlink(vnode, symlink); + } + + if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs))) { + netfs_free_folioq_buffer(vnode->directory); + vnode->directory =3D NULL; + vnode->directory_size =3D 0; + } + + return ret; +} + +static ssize_t afs_read_symlink(struct afs_vnode *vnode) +{ + ssize_t ret; + + fscache_use_cookie(afs_vnode_cache(vnode), false); + ret =3D afs_do_read_symlink(vnode); + fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL); + return ret; +} + +static void afs_put_link(void *arg) +{ + afs_put_symlink(arg); +} + +const char *afs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *callback) +{ + struct afs_symlink *symlink; + struct afs_vnode *vnode =3D AFS_FS_I(inode); + ssize_t ret; + + if (!dentry) { + /* RCU pathwalk. */ + symlink =3D rcu_dereference(vnode->symlink); + if (!symlink || !afs_check_validity(vnode)) + return ERR_PTR(-ECHILD); + set_delayed_call(callback, NULL, NULL); + return symlink->content; + } + + if (vnode->symlink) { + ret =3D afs_validate(vnode, NULL); + if (ret < 0) + return ERR_PTR(ret); + + down_read(&vnode->validate_lock); + if (vnode->symlink) + goto good; + up_read(&vnode->validate_lock); + } + + if (down_write_killable(&vnode->validate_lock) < 0) + return ERR_PTR(-ERESTARTSYS); + if (!vnode->symlink) { + ret =3D afs_read_symlink(vnode); + if (ret < 0) { + up_write(&vnode->validate_lock); + return ERR_PTR(ret); + } + } + + downgrade_write(&vnode->validate_lock); +=09 +good: + symlink =3D rcu_dereference_protected(vnode->symlink, + lockdep_is_held(&vnode->validate_lock)); + refcount_inc(&symlink->ref); + up_read(&vnode->validate_lock); + + set_delayed_call(callback, afs_put_link, symlink); + return symlink->content; +} + +int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +{ + DEFINE_DELAYED_CALL(done); + const char *content; + int len; + + content =3D afs_get_link(dentry, d_inode(dentry), &done); + if (IS_ERR(content)) { + do_delayed_call(&done); + return PTR_ERR(content); + } + + len =3D umin(strlen(content), buflen); + if (copy_to_user(buffer, content, len)) + len =3D -EFAULT; + do_delayed_call(&done); + return len; +} + +/* + * Write the symlink contents to the cache as a single blob. We then throw + * away the page we used to receive it. + */ +int afs_symlink_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct afs_vnode *vnode =3D AFS_FS_I(mapping->host); + struct iov_iter iter; + int ret =3D 0; + + if (!down_read_trylock(&vnode->validate_lock)) { + if (wbc->sync_mode =3D=3D WB_SYNC_NONE) { + /* The VFS will have undirtied the inode. */ + netfs_single_mark_inode_dirty(&vnode->netfs.inode); + return 0; + } + down_read(&vnode->validate_lock); + } + + if (vnode->directory && + atomic64_read(&vnode->cb_expires_at) !=3D AFS_NO_CB_PROMISE) { + iov_iter_folio_queue(&iter, ITER_SOURCE, vnode->directory, 0, 0, + i_size_read(&vnode->netfs.inode)); + ret =3D netfs_writeback_single(mapping, wbc, &iter); + } + + if (ret =3D=3D 0) { + netfs_free_folioq_buffer(vnode->directory); + vnode->directory =3D NULL; + vnode->directory_size =3D 0; + } else if (ret =3D=3D 1) { + ret =3D 0; /* Skipped write due to lock conflict. */ + } + + up_read(&vnode->validate_lock); + return ret; +} + +const struct inode_operations afs_symlink_inode_operations =3D { + .get_link =3D afs_get_link, + .readlink =3D afs_readlink, +}; + +const struct address_space_operations afs_symlink_aops =3D { + .writepages =3D afs_symlink_writepages, +}; diff --git a/fs/afs/validation.c b/fs/afs/validation.c index 0ba8336c9025..743f19d6ec39 100644 --- a/fs/afs/validation.c +++ b/fs/afs/validation.c @@ -468,8 +468,12 @@ int afs_validate(struct afs_vnode *vnode, struct key *= key) /* if the vnode's data version number changed then its contents are * different */ zap |=3D test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); - if (zap) - afs_zap_data(vnode); + if (zap) { + if (S_ISREG(vnode->netfs.inode.i_mode)) + afs_zap_data(vnode); + else + afs_invalidate_symlink(vnode); + } up_write(&vnode->validate_lock); _leave(" =3D 0"); return 0; diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index 24fb562ebd33..d941179730a9 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -960,7 +960,7 @@ void yfs_fs_symlink(struct afs_operation *op) =20 _enter(""); =20 - contents_sz =3D strlen(op->create.symlink); + contents_sz =3D strlen(op->create.symlink->content); call =3D afs_alloc_flat_call(op->net, &yfs_RXYFSSymlink, sizeof(__be32) + sizeof(struct yfs_xdr_RPCFlags) + @@ -981,7 +981,7 @@ void yfs_fs_symlink(struct afs_operation *op) bp =3D xdr_encode_u32(bp, 0); /* RPC flags */ bp =3D xdr_encode_YFSFid(bp, &dvp->fid); bp =3D xdr_encode_name(bp, name); - bp =3D xdr_encode_string(bp, op->create.symlink, contents_sz); + bp =3D xdr_encode_string(bp, op->create.symlink->content, contents_sz); bp =3D xdr_encode_YFSStoreStatus(bp, &mode, &op->mtime); yfs_check_req(call, bp);