From nobody Wed Jun 17 06:03:54 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 407C63385BE for ; Mon, 27 Apr 2026 15:46:54 +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=1777304816; cv=none; b=Dc5MopIEU+K5ejIn/+FkxEEvV4cD/zZI440urTGs2Jft6pNsmok4nC6ioljpg2oanCuvjgVH0oo/HXqaRQ4o7PjevSmbjeEOwhL6uEDHp3IDW0vJNAbpqWbRxmzVBktNFp1JdYIDP0KBZQQ7cTAl3WIq1+RZo2FpfQ5OFFxkC2s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304816; c=relaxed/simple; bh=MBV+gcvxIoEEQ+AAmirehOHfdTWrL3EvsI56zQBg4Ak=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FlOgp1SDXdvKy1N9Wujpr8wAfpGr8EwvU0stubvqcv+9NoM1J6xQX6ZutiKGOxKcvEsqYBTb+oA05HGc7Vajup8bH7oUdwzX/O0f5Rv7FfiUQ2T1Kvbxo/rgl2hjBXayPxloaI2F5nsZcJhIypDpAz2jXWulnKJ3p0mVDGvYSDI= 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=XZiNVdrr; 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="XZiNVdrr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304814; 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=u9RMsSgh/DXZPAvKktBs27mDcdJgglZsfz7/ceNESHA=; b=XZiNVdrrz7uQ/PxIIyTQ/E/X/XlAaPQXE9E53gqWVXDVRchGzpQVg9yKFEtYFezlwkDTav eYkT00WXb6KjjQ1p85Bbm7uBtfd/h3Z5iMLN4tvEbVYNIbTgea6t8X95pP9GvEcAuWJJjx ZXVj6PI8cppYtjFJDK4E44it2zosE2s= 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-121-2k7FS481MReU8Yefq02IXA-1; Mon, 27 Apr 2026 11:46:51 -0400 X-MC-Unique: 2k7FS481MReU8Yefq02IXA-1 X-Mimecast-MFC-AGG-ID: 2k7FS481MReU8Yefq02IXA_1777304809 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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 90C2918002C0; Mon, 27 Apr 2026 15:46:49 +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 DD1CF1800446; Mon, 27 Apr 2026 15:46:46 +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 v4 01/22] netfs: Fix cancellation of a DIO and single read subrequests Date: Mon, 27 Apr 2026 16:46:16 +0100 Message-ID: <20260427154639.180684-2-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 | 26 +++++++++++--------------- fs/netfs/direct_read.c | 19 ++++--------------- fs/netfs/internal.h | 2 ++ fs/netfs/read_single.c | 20 ++++++++------------ 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index a8c0d86118c5..2c51c55a9b15 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) { @@ -262,11 +258,8 @@ static void netfs_read_to_pagecache(struct netfs_io_re= quest *rreq, 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); + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + netfs_read_subreq_terminated(subreq); break; } trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); @@ -302,10 +295,13 @@ static void netfs_read_to_pagecache(struct netfs_io_r= equest *rreq, netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); 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..4fd5cfa690cf 100644 --- a/fs/netfs/direct_read.c +++ b/fs/netfs/direct_read.c @@ -47,7 +47,6 @@ static void netfs_prepare_dio_read_iterator(struct netfs_= io_subrequest *subreq) */ static int 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; @@ -66,25 +65,15 @@ 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); + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + subreq->error =3D ret; + netfs_read_subreq_terminated(subreq); break; } } diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index d436e20d3418..24fefa1b179d 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); diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c index d0e23bc42445..432c7456a1b6 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; @@ -137,11 +133,11 @@ static int netfs_single_dispatch_read(struct netfs_io= _request *rreq) break; } =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); + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + subreq->error =3D ret; + netfs_read_subreq_terminated(subreq); return ret; } From nobody Wed Jun 17 06:03:54 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 D59DD34B437 for ; Mon, 27 Apr 2026 15:46:59 +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=1777304821; cv=none; b=d8xSAXm4nYLQexAAzMW4+lKm9cCbEK5TjFBXEXozvuSdkLOIT6fSn8B8kEMhLHFnOSB8/sYYcUI+gL6Hhw4TSMQCxDKIzMPaipZomSxB0kO50VtoCbAJh2wg4PH2feNZYGZMH+JOb0K5lJZKeH6aL1DGsvMh5yqrTzAiJeCk8Lw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304821; c=relaxed/simple; bh=7FzhUf9knCCl7nFRHbgQohO+ezm/DS9Bo3Dnzsq+An4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=U2wFADaYGmkBe8CtMvV2mtdd35b1ZQOwyaVmrn7X3gK68Ei+wnCj+AVd+D130V7wrOV4S2OqBnGvGIlW542zL+trOe/F1AdCzRC+C4yX/Jotb9lfVX9044Z1MZ26pPQj0r1dOhoNOqOBk6C+YIOS4ETNGAV8BO1Z210HCgd+jQA= 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=UJuB2wcx; 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="UJuB2wcx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304819; 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=UyovM53z5LrYXAn1v0rqTsjGNE7y4XtHURaRJHW+mN0=; b=UJuB2wcxDxsGMctieEVfoiMPN++ep4pbf3LHR4hbWGUIyWo1POk7Hq6NzUgZV6+rIszOcw ITej+OBg4e/Q8nBeCf2UNIvnK/kWvnQ+YFjlT3VFRSq2ioixMSCMP0B/9l/joOKrFiWTHF 78OWaMuxzizSqNYfQxxMbcBkqWtlmAY= 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-244-kPASUYywPqGGqL34bbfgeQ-1; Mon, 27 Apr 2026 11:46:55 -0400 X-MC-Unique: kPASUYywPqGGqL34bbfgeQ-1 X-Mimecast-MFC-AGG-ID: kPASUYywPqGGqL34bbfgeQ_1777304814 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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id ED8A218005AB; Mon, 27 Apr 2026 15:46:53 +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 465111800446; Mon, 27 Apr 2026 15:46:51 +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 v4 02/22] netfs: Fix missing barriers when accessing stream->subrequests locklessly Date: Mon, 27 Apr 2026 16:46:17 +0100 Message-ID: <20260427154639.180684-3-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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_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_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/read_collect.c | 4 +++- fs/netfs/write_collect.c | 4 +++- fs/netfs/write_issue.c | 3 ++- include/linux/list.h | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 2c51c55a9b15..3bc7d0c5c3b9 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/read_collect.c b/fs/netfs/read_collect.c index e5f6665b3341..f6b87a22c290 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, + front =3D list_first_entry_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..ba4ac6993b74 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, + front =3D list_first_entry_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..5af356efd725 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_acquire - get the first element from a list with barri= er + * @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_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 Wed Jun 17 06:03:54 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 EF8DB342519 for ; Mon, 27 Apr 2026 15:47:03 +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=1777304825; cv=none; b=h9HENISbRoSAGXjNJQGkaYsE60McMP3kPaQzE8qUAzRjM+Q4kOY6Jy2xUOXw5ZSqvJcHaVwOAB/WH2lllBO/4cm/A3u/3YVSGcz1rocC9+NsIOPd/I0l+ahyi7EwyCEWpXyPAYwW9r0lrGGtdRt8/GuMtlWhKs27Agg1GC99WB8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304825; c=relaxed/simple; bh=TzKE7QcFZf39E2UkvuI1rxQbsvby4wW7m4gGjN+BIzs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=g+W394ySe6VokB2Aw59LcQMhoZrIOMKwpzZYgXzmfNJPTYkmO2jhC+pEOK1MRaz0AAFV0mTPOPf6c3yrWA+I6JHoth2Gk3BZAeVYBR5CBnXU1tsrK9j2rO05Yx0XLgT6AtNtyZ0fKpqd/WWiGDry17TiOO8Qa9p79lysBR0JBOI= 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=WjTZu/s5; 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="WjTZu/s5" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304823; 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=REMGICrXXI7qiSN2rIwTeEeIpWS2D3QtA/yikvDYdpo=; b=WjTZu/s5Zio77KNJjlLYqvSPHDu2DAX+Q/KmlXIFkg5ogfpLiwBkf3CbF4h5blDC/7eX9y eElWyuaYZcmYED77Q9D+xmGLqJhnawWrxaHZmecutQSDTyJ+u+/YQlmw0bn0GVeohE5T5x NyUHVh/Wlk/p/lGV38liNf5WZ9aQTOM= 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-479-dTFzzbiiPRWoZ3I7QBqWfw-1; Mon, 27 Apr 2026 11:46:59 -0400 X-MC-Unique: dTFzzbiiPRWoZ3I7QBqWfw-1 X-Mimecast-MFC-AGG-ID: dTFzzbiiPRWoZ3I7QBqWfw_1777304818 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 5A43819560A7; Mon, 27 Apr 2026 15:46:58 +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 A457B1800348; Mon, 27 Apr 2026 15:46:55 +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 v4 03/22] netfs: Fix missing locking around retry adding new subreqs Date: Mon, 27 Apr 2026 16:46:18 +0100 Message-ID: <20260427154639.180684-4-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 | 2 ++ fs/netfs/write_retry.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index cca9ac43c077..b34561e257f0 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -203,7 +203,9 @@ static void netfs_retry_read_subrequests(struct netfs_i= o_request *rreq) refcount_read(&subreq->ref), netfs_sreq_trace_new); =20 + spin_lock(&rreq->lock); list_add(&subreq->rreq_link, &to->rreq_link); + spin_unlock(&rreq->lock); to =3D list_next_entry(to, rreq_link); trace_netfs_sreq(subreq, netfs_sreq_trace_retry); =20 diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c index 29489a23a220..db0f23708b2d 100644 --- a/fs/netfs/write_retry.c +++ b/fs/netfs/write_retry.c @@ -153,7 +153,9 @@ static void netfs_retry_write_stream(struct netfs_io_re= quest *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); + spin_unlock(&wreq->lock); to =3D list_next_entry(to, rreq_link); trace_netfs_sreq(subreq, netfs_sreq_trace_retry); From nobody Wed Jun 17 06:03:54 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 3EBB4342517 for ; Mon, 27 Apr 2026 15:47:08 +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=1777304830; cv=none; b=Z6zVtO72/fLXIpm3japtOz8tNkOCY+buRGQKnE1M10G9U3ss7Uf+XJJQOz/A8kS1KEeiZeRDy4YZl7PHyUuClsp+gWWj1zBwddAhMdNIbYqC1jZ1uP/jWjoktWw8vPI5kpyZxUkDVwp23iCd+5/wJ06yALSArULkW7gGGKJmuPM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304830; c=relaxed/simple; bh=4vDqPI8DVk4MKXgzTTYnAtb5Hin/PlJJp6BgfgK/hjI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sJYAjZXq8m0T3dQB1AT0t93h38lQkdGjjJelmQXuVUyjOaoUch7Wp/PSkfpFCv4EWEYyNNqQio4LHqosJHlRF1jvIBB3eHAXExyWhfpAWLQNI7JGyiqlaCTIwSkJcLYBejKyhBzkaly2l5C305HFBGBjenNnYQUdAwy+dQc7N2I= 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=EZppV8DS; 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="EZppV8DS" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304827; 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=F1+6dfsU9EA3zra9BmDxqCEbg56ER/RAJitXxHpONYg=; b=EZppV8DSvnkGt7XiNFilNUH0Jx1LTC2dJeF8TtyeZy75hGTU6MZnLbWwCbqToMFl/3k+fG HDWXp4KZxTLfrV/Gr2OnJGxXZtBH3qs+yvSIao6F5grH7b2PI8LQG3ysJv2nnHVmb1jhK8 8HsBM2EKMe9DIy0FTBzSfbTPDbmxZpo= 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-660-M65CsH5jPWKe8idtZ72r4Q-1; Mon, 27 Apr 2026 11:47:04 -0400 X-MC-Unique: M65CsH5jPWKe8idtZ72r4Q-1 X-Mimecast-MFC-AGG-ID: M65CsH5jPWKe8idtZ72r4Q_1777304822 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 B0DDD18003FC; Mon, 27 Apr 2026 15:47:02 +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 EF4EC1800348; Mon, 27 Apr 2026 15:46:59 +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 v4 04/22] netfs: Fix netfs_read_to_pagecache() to pause on subreq failure Date: Mon, 27 Apr 2026 16:46:19 +0100 Message-ID: <20260427154639.180684-5-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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_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 3bc7d0c5c3b9..9c7a2f984be9 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -304,6 +304,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 Wed Jun 17 06:03:54 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 503E0346766 for ; Mon, 27 Apr 2026 15:47:14 +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=1777304836; cv=none; b=jnqMHbo/kKUQ6RLxmNjsmPI1BaywfHumQ1uWJaaJsfZ8bo60ity3EXq3qR60mJXb9wB98DIi205cjW2ihrbdo/WBkl2x5SexVUMDi3K4UHA4urlD5dDqu37usVfvC1rG6bhE28u6CxMIsNYVOfoOIg+icaLkJPPjAR9tls5hkCg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304836; c=relaxed/simple; bh=MFXt9+2Qy6Ow0V2614gz6kIwLWy9bmUDT6n6YXdru98=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JblRikxQs8ltpchoOECWmAuEt+uRs1al3mr3s4Czc53m649sOWvVWXetMZ3q7FFY0uFlyZA1FGomvlIIwVLdjW67/47RusDh//zrsub79fOLrLyvGL6ZenPMgylY8wKmGUl1+qOpX+y1U1DCtFx7gnBfNoS0bTpuDkuNcQLUQHc= 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=NB/rqnC4; 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="NB/rqnC4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304833; 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=k5JBX/Wj/+XuOQekceKvNcYnkpGi88s4vX4RWLInoxk=; b=NB/rqnC4tIRmFi/0KplSGlFsO7I7G70mJfEBH0sD1dv0BuJBwm0mAGKHRR8XWsNRr+M6T1 F8v9YGtYcGy5pNeCfqiSsIs3CXhOTDBN2t6rXAAcNIupBjXk+2NoGLhzsLXrf0vI+gKlT6 Mqodn5u030erHs4ebYEUAduEKwDB1C8= 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-551-KucwLJinP6ut73hfArb1-w-1; Mon, 27 Apr 2026 11:47:09 -0400 X-MC-Unique: KucwLJinP6ut73hfArb1-w-1 X-Mimecast-MFC-AGG-ID: KucwLJinP6ut73hfArb1-w_1777304827 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 95C3C1800473; Mon, 27 Apr 2026 15:47:07 +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 64654195608E; Mon, 27 Apr 2026 15:47: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, Matthew Wilcox Subject: [PATCH v4 05/22] netfs: Fix potential for tearing in ->remote_i_size and ->zero_point Date: Mon, 27 Apr 2026 16:46:20 +0100 Message-ID: <20260427154639.180684-6-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 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 | 303 ++++++++++++++++++++++++++++++++++++-- 17 files changed, 354 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 9c7a2f984be9..3b26b8113401 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); break; } subreq->len =3D len; 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 6df89c92b10b..9d92d068f1da 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 ba4ac6993b74..f0cafa1d5835 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..c5a1e37ce55a 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(&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..90e061e444ce 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,264 @@ 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 - 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; + + preempt_disable(); + write_seqcount_begin(&inode->i_size_seqcount); + ictx->_remote_i_size =3D remote_i_size; + write_seqcount_end(&inode->i_size_seqcount); + preempt_enable(); +#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; + + preempt_disable(); + write_seqcount_begin(&inode->i_size_seqcount); + ictx->_zero_point =3D zero_point; + write_seqcount_end(&inode->i_size_seqcount); + preempt_enable(); +#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 (to > netfs_read_zero_point(ictx)) + netfs_write_zero_point(ictx, to); +} + +/** + * 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; + + preempt_disable(); + 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); + preempt_enable(); +#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 +746,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 +756,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 +769,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) + 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) - 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; + 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 Wed Jun 17 06:03:54 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 9657E34B682 for ; Mon, 27 Apr 2026 15:47:18 +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=1777304840; cv=none; b=Ee6FB+iylFPLKbOJSzQS3xZZC82sqnPb5agZE+vzT7d+0NFFQcik2WsZ8UMKYBFRJ0yLQRyiqezQj3b3RpqIb6LFnS48xya3od+ANopWMXoTLA8nTmA2Ffu7KztOwEPucUXM+ob4tPvIMv2aQcZeaUT7COm5RZVsMEJzsbELA7E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304840; c=relaxed/simple; bh=o05Io1xRtBayWGYR2oc3Xg7A3FtUEcWOi0kE+EBlUgg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Cf+vzo1/2Z1gXLFmXg557j4DNrYa23Cy/n2dgd4kZKj72UFSKTRp1Kj4MfZI9168mprwVy7E0DFUowsqJZWw1tIx0cvf2Jsr+TbrqAR3zgC9zFmgU8L6QUJSv4SKVUe2S2D+5WG9CMO26CyPxVcut+aB+lDYgH2U8i34Hi5NrW4= 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=Ylfa61F4; 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="Ylfa61F4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304837; 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=47TW6xAuGaXgyo+FT4sWhE5OZ+F3gm9XYQOGltuTIYQ=; b=Ylfa61F4xH1sp621jSVjiJU2Hm01FlPt6Ar1KUW53KIjN2yTseyNlSluWlCmE416SSAJEP zlnGlk9YElnZiEMHiGmbEpgZnmXx11Uzvu32ALqz0o9E8pIdE2LdH7x9iFU4dezybuDS2C nVoOBiIQiuM1ZsKEaCynVAbKNXwu6vU= 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-433-aLkI0RYNNYODQd5P3S-gDg-1; Mon, 27 Apr 2026 11:47:13 -0400 X-MC-Unique: aLkI0RYNNYODQd5P3S-gDg-1 X-Mimecast-MFC-AGG-ID: aLkI0RYNNYODQd5P3S-gDg_1777304832 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 394E31956088; Mon, 27 Apr 2026 15:47:12 +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 4840D1800446; Mon, 27 Apr 2026 15:47:09 +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 v4 06/22] netfs: Fix zeropoint update where i_size > remote_i_size Date: Mon, 27 Apr 2026 16:46:21 +0100 Message-ID: <20260427154639.180684-7-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 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 ++-- include/linux/netfs.h | 35 +++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 9d92d068f1da..37d9651078e6 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; diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 90e061e444ce..59f35d2eeb2e 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -530,11 +530,11 @@ static inline void netfs_write_remote_i_size(struct n= etfs_inode *ictx, #if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) struct inode *inode =3D &ictx->inode; =20 - preempt_disable(); + 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); - preempt_enable(); + spin_unlock(&inode->i_lock); #elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) preempt_disable(); ictx->_remote_i_size =3D remote_i_size; @@ -605,11 +605,11 @@ static inline void netfs_write_zero_point(struct netf= s_inode *ictx, #if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) struct inode *inode =3D &ictx->inode; =20 - preempt_disable(); + 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); - preempt_enable(); + spin_unlock(&inode->i_lock); #elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) preempt_disable(); ictx->_zero_point =3D zero_point; @@ -635,8 +635,27 @@ static inline void netfs_write_zero_point(struct netfs= _inode *ictx, static inline void netfs_push_back_zero_point(struct netfs_inode *ictx, unsigned long long to) { - if (to > netfs_read_zero_point(ictx)) - netfs_write_zero_point(ictx, to); +#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); + 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 old =3D ictx->_zero_point; + + while (to > old) { + old =3D cmpxchg_release(&ictx->_zero_point, old, to); + } +#endif } =20 /** @@ -709,12 +728,12 @@ static inline void netfs_write_sizes(struct netfs_ino= de *ictx, #if BITS_PER_LONG=3D=3D32 && defined(CONFIG_SMP) struct inode *inode =3D &ictx->inode; =20 - preempt_disable(); + 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); - preempt_enable(); + spin_unlock(&inode->i_lock); #elif BITS_PER_LONG=3D=3D32 && defined(CONFIG_PREEMPTION) preempt_disable(); ictx->_remote_i_size =3D remote_i_size; From nobody Wed Jun 17 06:03:54 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 610D135B650 for ; Mon, 27 Apr 2026 15:47:23 +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=1777304845; cv=none; b=pYCXXENmZWIlzcqniCJ3uwo+pS7R7fl+uQFuV3eegWYaRG6K2xarCgzIspjM6esUEmdmOgNXgoMCWhM2dAnK7AdYk7NNME1m53QSXhl7ssumw/ShnmzrfOqx47CScWikBRIPa3vRB3/0qorm22AfdPTkV9cj5rv5/Au+S3V4Y5c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304845; c=relaxed/simple; bh=UFCJ7w5t/grCRjVENJVso8NyqD2Sd1uOcf+ESoWhjuM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nPFTCLYTRpUU/7ipRNztDtYs9VvtsbP4YGujMc55Hcs4UOH02Vq5wE2zvpPg1T0xJcRPZyBkRlCbhH5ddCxxud05ALbDVx/JkhlM0Zk2iGdxfp/jGSSpnZEtGvhk7Kgyfg2xEfIhzYOE/HhxBbqB1O2VF1+sIz4kdIvrzo/8jRc= 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=P21fD54K; 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="P21fD54K" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304842; 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=o4o0pRb1pfLGYd4DJ46WsX54kfc9t1T33IsrIAMnM84=; b=P21fD54KbF5wtswecYU0icv+oUBCx24bbNh5oafO84h4FTSd1JSq237BjmTUkd9l9ujf0C PduCpzErYWN7Ar+1rb1zqAcXdmGM3ok8EhheJbxqqWkSmnqJsTpUHDvm00FGn3VUZShQgp ykTMvz6O5bvtEaIresYJ/uJxC4JDQxI= 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-XmKIqnwQOJWK3IxlnygUqQ-1; Mon, 27 Apr 2026 11:47:18 -0400 X-MC-Unique: XmKIqnwQOJWK3IxlnygUqQ-1 X-Mimecast-MFC-AGG-ID: XmKIqnwQOJWK3IxlnygUqQ_1777304837 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-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DA23119560B8; Mon, 27 Apr 2026 15:47:16 +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 E7766196B8FB; Mon, 27 Apr 2026 15:47:13 +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 v4 07/22] netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call Date: Mon, 27 Apr 2026 16:46:22 +0100 Message-ID: <20260427154639.180684-8-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 b34561e257f0..6e5e0da88290 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -290,8 +290,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 Wed Jun 17 06:03:54 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 19B0D374E5C for ; Mon, 27 Apr 2026 15:47:27 +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=1777304848; cv=none; b=i/l2y809TM2QTDzhfBKpJX3sti15rE9F1woZQjFtsyL9+3IDslzizSERmdk07NzvLr8vJp43RDW5/G1JUcHYbw4p7BbVrkQfeLjtzL77Dk68ojpLLz9amPKG5+TG95b+HKU3IdzJ3qLoNvUHh3FszNA5tC7IsUB+wSWT+kyeQhs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304848; c=relaxed/simple; bh=S62t+upgojIYhz1p6dALu+kyrentByS35lQhzLSLXbY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RRuc/44a77t1HT4Nz2ST/sV3zyv13CzJt9QZzUvN6cQXWKuQFJ0fEzy6HyPCpDXb1dUzH5bzlzUTE+v8lLFaDefY8AFQRyhcKGJrCTYS+FimjZQ5FxeNYxCtQpmHptiOZAZdEseSPLZ/0reFxpePmzYvUvsb5IdJmNGY5xHzrsU= 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=aw4k8kQ/; 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="aw4k8kQ/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304846; 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=aw4k8kQ/j076bXUEQbA7JAgqUuERBETSGIINm35jJ512J92PiqQl4pjxQxozw5nl3Cl5CZ /EO9j0kZxiDhZGsTb5aruEtD5YviDv1GXXu2qE4epVR3r4TpdEDgxEeChsjX+IjS8HTAcy k6SlFvXxj0CP2QO1ulrxWr+/inV4Xzo= 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-232-OTI0gsoYPxydAzb8QCnzPg-1; Mon, 27 Apr 2026 11:47:23 -0400 X-MC-Unique: OTI0gsoYPxydAzb8QCnzPg-1 X-Mimecast-MFC-AGG-ID: OTI0gsoYPxydAzb8QCnzPg_1777304841 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 BCA0F18005B1; Mon, 27 Apr 2026 15:47: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 8DFEB19560AB; Mon, 27 Apr 2026 15:47: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, Xiaoli Feng , stable@vger.kernel.org Subject: [PATCH v4 08/22] netfs: fix error handling in netfs_extract_user_iter() Date: Mon, 27 Apr 2026 16:46:23 +0100 Message-ID: <20260427154639.180684-9-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 Wed Jun 17 06:03:54 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 AE7D934D91F for ; Mon, 27 Apr 2026 15:47:31 +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=1777304853; cv=none; b=SO/ym+k1oEQc06UpeNyMYz/oXNHCty1cHIcON/Iq8hiLJ/xIESMvkwsHZl5j2y72hhptNZ1RIB+pKYBKbi97eLAUU04NH5IuoO946HDLzU7UGHsK7wY/UNmq2G0E0DzCnZ74l5I6WfU5QzfTi7B74TRFwu/7zvaEjFdABdv0j+g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304853; c=relaxed/simple; bh=VrBMZhmWiHPaD8tO+9hsAivp0KvomApYCwcuJnCGxUs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZmpELSv9Teto/1wxFAgmeNUBaF1FFPZBlHK9rtC4Uqa4RtYezwGg7R+TgwONndODDjoaTiIrZec57xOscVfkbfPZ8bKO8C0yA2fqm/lzW55rZxvqOnZUBaAw/rIgUQRg1RojunKSbBzNdI3pYPoSeJhAMx3hoDSgGV8w3uoRNuw= 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=cm6gtEIb; 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="cm6gtEIb" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304850; 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=xJlXFm6gho8MPz3RsbSXNfFwZ2mKHUMmXSQpBOBJ+wU=; b=cm6gtEIbCtuwHB1LWSh5TYYHGKeR21gflnMxUY/TtN+hVERCw48dgmRjQDOuh7ftsWMHSm DaRWsKt9Hd9Z5NMIDN79TXasO6RR21HWoR1V/ANhbvB8zUTM4bnoNyzpb1RdZ5VK+enyvO APZEf0ezqUqm1u7SBd4g18DULN7DFxY= 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-482-X1P5x3TgN0-Lo6n3wv9-pA-1; Mon, 27 Apr 2026 11:47:28 -0400 X-MC-Unique: X1P5x3TgN0-Lo6n3wv9-pA-1 X-Mimecast-MFC-AGG-ID: X1P5x3TgN0-Lo6n3wv9-pA_1777304846 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 A38A718005AA; Mon, 27 Apr 2026 15:47:26 +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 5D1E619560AB; Mon, 27 Apr 2026 15:47: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, Marc Dionne , Matthew Wilcox Subject: [PATCH v4 09/22] netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone Date: Mon, 27 Apr 2026 16:46:24 +0100 Message-ID: <20260427154639.180684-10-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 37d9651078e6..4e91a8d75bce 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 Wed Jun 17 06:03:54 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 0CFA834EEE5 for ; Mon, 27 Apr 2026 15:47:38 +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=1777304860; cv=none; b=sV9EyJAu3Fi5cHtsOnrhUQXlueI3OaVHofWQdsUvRrumw4uG5dF/yVtSat1M4WRyDM5Pn5GjcAFVZ5AmoB3AOxOIk5k49WwmlrGQIPFCHXn/Jghg6sSjOn8frIMVqvROtgzm3AavS84XXLlZWuR5BdccXQ6UYlmuis+QS5vUe2U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304860; c=relaxed/simple; bh=Hls64bsLlj6noJlM3TRUh03Fmmi4w1KwMCcwqEYCqZ4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W28IyKkMo0RM/vWDTrpciNk3oIGy3j98E6LI+LhBICTl3OT18hZ/TVtmNNzH2HaMmnjOFR1tJY+8hYjsRLHhT4Bsn7nXJ+/de2aJzXEjHpIO5nTbN8DX2APjS2NU1TmlHv+wtv515XhlDIxrYMPVY04pBPL4Np028otiqKpbMzg= 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=LB1gudHM; 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="LB1gudHM" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304858; 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=LB1gudHMvCj4BROlpHMf+ODFgo6XHaQBl2byZgkPP65eO4xkTJiGp/pzDZTh5O4so1l6/C VJubTkag/1gUK37nNhbGKMUHnpDQ5ldKxX0pZRqDbrQiIYs9+YXZiXaWJMzk54WdC1rirB uSDmGiYHWToJ/mbCEoRHea1D0a7//Kc= 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-509-Ih5o6OdTN4OYlt2BEEDXww-1; Mon, 27 Apr 2026 11:47:33 -0400 X-MC-Unique: Ih5o6OdTN4OYlt2BEEDXww-1 X-Mimecast-MFC-AGG-ID: Ih5o6OdTN4OYlt2BEEDXww_1777304851 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 914F719540FE; Mon, 27 Apr 2026 15:47:31 +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 41ADC300070A; Mon, 27 Apr 2026 15:47:28 +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 v4 10/22] netfs: Defer the emission of trace_netfs_folio() Date: Mon, 27 Apr 2026 16:46:25 +0100 Message-ID: <20260427154639.180684-11-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 Wed Jun 17 06:03:54 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 DFD083BFE50 for ; Mon, 27 Apr 2026 15:47:42 +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=1777304864; cv=none; b=irjZ+5BZOd5Ks8vabGNSyfrDvpTHPvevxWSoeZ1f/MdvW6qlJew5bDmSRUGwfDsHNp+iYC3T8Mli5zaZkHvwlUJneXj3Hl/zps3Vmb6RUYjwYFSFPBMHubOD7atoDG9zTF+xUQK2ZOk8MxW3jKBvOJcb6l8OQpWQu1U5on/5T6Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304864; c=relaxed/simple; bh=3qCrcikapOBjDiKHYNn87mJHSh0bvZvMd8FSy9FXXaw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l1iobm7chGNu5sFzwvHJ72dr/LDmPymkyisyuDxYFF4P8i118OSZy+piFkBBqpJBPoFYwWW6o325CWkRuOaZTmz0E0bjVyvxjrmnD//5NX8VTaQtM2W9U0d3JeReu1HIzTOEDchZJxJT7lA902l7/uQUFYVedLBZyMQMbykJBG8= 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=W4exd6uc; 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="W4exd6uc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304862; 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=W4exd6ucYBrR4RVk2MN8e/zobWXCHb0ajc3j9lzvq43xGHv/xIgYqeHkZbZ1kAH3PLM00b fI2ndU/B9r2jbXQ2tg90k+ucmCKJu/3Rq9bqnsaTqhJ1mfQHu7bzABbuPQsNVCg00b3BQ/ BR+yuS8X0I3yvjFzOr+GEQX+iQpe8EA= 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-644-wWUMDLLwNb6msJBw_p3cWA-1; Mon, 27 Apr 2026 11:47:38 -0400 X-MC-Unique: wWUMDLLwNb6msJBw_p3cWA-1 X-Mimecast-MFC-AGG-ID: wWUMDLLwNb6msJBw_p3cWA_1777304856 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 356AB195608F; Mon, 27 Apr 2026 15:47:36 +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 4784D1800906; Mon, 27 Apr 2026 15:47:33 +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 v4 11/22] netfs: Fix streaming write being overwritten Date: Mon, 27 Apr 2026 16:46:26 +0100 Message-ID: <20260427154639.180684-12-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 Wed Jun 17 06:03:54 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 4881A3BB9F5 for ; Mon, 27 Apr 2026 15:47: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=1777304867; cv=none; b=MSA/ZwTm41ISdEHHR8f3InW9gLTgmJGSOYAee+t8JvZgcTWyGhxVeLSIMs2tALyNuh3bksRkllm4Ae5BIkJZ+sIbUTZu+v/mXf7c7H+bxSghBpvqwVqZh/lzCMEhcivByX9LH3RugzoCwco3hsErSz5A7y3d6lIFG1a06gCL3LY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304867; c=relaxed/simple; bh=Us2Q19DvcJquz3a09IEqW6WuYt1jFkjLNcIkoFQc8s0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UYe+X9q8XSgfr7DDOdeKLNA3NCVdJuS+Tft2bTIyi+NaJ33gAkiCNAqK5ZypEQHUsi3soEM0Vkh/60Rxn8tte75qKofZSpdseRuQbtabJThrhGZDYdyzZz6RSUcTh7/iSBAqvo81yXJX2Pm4e/gvtw3LHsbddx/sGNFG3MyX1oc= 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=e6028zr4; 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="e6028zr4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304865; 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=YMJCGQjgBF59xwSVUpUZlzOMstVEkqoAU/oKa2PZXqk=; b=e6028zr4S7qfOxwxT4Nugl+IaOjwZlwqkeaxOHv+L/o+wWZBdREU/guSl6IuNz5Gp+z4Tx cHAWhbJOoV5AD9oVsD84TCv59AfBfMUpIsDadTLiY7jjMOrfWGX9YLrW0JcAeYesK/oAv/ D3ojC6kbvepOa4N56Z1CNuWzYDCLHNs= 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-380-b9j1WSlZO7OME4jzDoPg8Q-1; Mon, 27 Apr 2026 11:47:42 -0400 X-MC-Unique: b9j1WSlZO7OME4jzDoPg8Q-1 X-Mimecast-MFC-AGG-ID: b9j1WSlZO7OME4jzDoPg8Q_1777304860 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 CF5431800648; Mon, 27 Apr 2026 15:47:40 +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 DF3CD19560AB; Mon, 27 Apr 2026 15:47:37 +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 v4 12/22] netfs: Fix read-gaps to remove netfs_folio from filled folio Date: Mon, 27 Apr 2026 16:46:27 +0100 Message-ID: <20260427154639.180684-13-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 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 3b26b8113401..600816914b46 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -400,6 +400,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; @@ -466,6 +467,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); } @@ -501,10 +508,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 Wed Jun 17 06:03:54 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 176AD348898 for ; Mon, 27 Apr 2026 15:47: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=1777304873; cv=none; b=S8eMwl8cbg8AdR3u0MfeyufMXrzEOREajwfGzshWhya1dYzO2npuOiYdmbLT+jBidT2X8qcbt2oQ1vXJwxqSA4LY/2UIFoqY9yZnKqyoHRgihTK3TRfn3zmNolCSBq8YQLsuNZ62bwlSl5XAZBRyUWPqjwgH/KoNA9jy9uS71yw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304873; c=relaxed/simple; bh=9e/Zee3jsbyQYvRQlmBnakDHnAEM9AymBLo2/24xEPk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ft7WIh3DhJuv6xZP9py2qcg57wpU9cGbpljYfrZNjdrxkKJ1h/XKFBravzV0dihyL1Fv/ICI0oLR8d8Fdz8C9NY9ZjL6r/cGpyXWsoT3jrHZWjyHs+2uNspSg/ys63/CR07LTv4J2dOO0w2OHKNnG+nNP0MVvgyoWGmZz/hoSjg= 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=W0jxmKVF; 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="W0jxmKVF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304871; 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=G5h8/9IhmPxVMQiVFJHtf2MmfhLnv0hhxcR+5vk8qZ8=; b=W0jxmKVFRljHB/kTCH7IV3G00KMfS78SzM1hLRMsN2VStnF4E/gn4EUilg9TwDHvQ13+gr rNbafr/t4EMCJfC72xkqqyHXuJl+NKWSWtJU9X2fCWFOm3Nl7Kge2CFbBY7F9gmz3cczHw /+IQ1jW0PJURO6g1ShIS9sjQWh86KrA= 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-653-B1v2nlguOQezFfXh3XpowA-1; Mon, 27 Apr 2026 11:47:47 -0400 X-MC-Unique: B1v2nlguOQezFfXh3XpowA-1 X-Mimecast-MFC-AGG-ID: B1v2nlguOQezFfXh3XpowA_1777304865 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 7BCF819560B0; Mon, 27 Apr 2026 15:47:45 +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 8A02D1800348; Mon, 27 Apr 2026 15:47:42 +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 v4 13/22] netfs: Fix write streaming disablement if fd open O_RDWR Date: Mon, 27 Apr 2026 16:46:28 +0100 Message-ID: <20260427154639.180684-14-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 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 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 25571a570ac9..b782cf83fb57 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -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 Wed Jun 17 06:03:54 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 74D5634F48D for ; Mon, 27 Apr 2026 15:47:56 +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=1777304877; cv=none; b=lroPT/JIzKRnJdiUtOkLvCQ9FNz3J/zUWYXOoQFq9Hyv1OxZifYT7tvabzLMBxAVI3+fVgy5ogHKdGdiQ1K7ENq2I4tnYACA55DK9daumJpHgz18QX8e16mXpDpe96SLD7Nrb/gPRzLCjTCiWcIHJv2NMtN9EGodSineXo+VBqc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304877; c=relaxed/simple; bh=7f3zZVgusl70g8Wkx147jd9t2v+xMRBABJ04d6Zne8c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=f7RHdZzt/8/PAoc3/JZuGGQ+8b9AQw7VdISWdy39E5PNfoNmG4h3EEX7QeX7HGFW/s2bnRupO2h+1u5WbuFDvQnRe4/z6ILiyrTbe6A7hhaJTS8yo7OcO0SRAq5cVXkrCE2t581cXj9ivFbvfrbCpidqPCw5jBfNeR2e2e3Bilk= 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=f5fvrYo6; 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="f5fvrYo6" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304875; 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=KH9Hgr6cDj7XA0YccXevVNloxVc6vWFCz16mYQEFJ7Q=; b=f5fvrYo6AdUW4TxsEOA3Lcj84b6J3aVPDQCirqFK3J0zoWZLxJDfZDJsjUrg8s1d4G6afa LlYzD1ZND+krWaTW4tulnztTp5uQ0/L7MzjwBPLHdApzAs253ofvoBB3V/SuTqGYgFrSfz SEX7+6/VcX8mGI1S1MuyH2aOMIK0dy0= 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-159-Hz5vaKE2P4OLh7z7yDX3jg-1; Mon, 27 Apr 2026 11:47:52 -0400 X-MC-Unique: Hz5vaKE2P4OLh7z7yDX3jg-1 X-Mimecast-MFC-AGG-ID: Hz5vaKE2P4OLh7z7yDX3jg_1777304870 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 9ECEC19560AA; Mon, 27 Apr 2026 15:47:50 +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 3EFA919560AB; Mon, 27 Apr 2026 15:47:47 +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 v4 14/22] netfs: Fix early put of sink folio in netfs_read_gaps() Date: Mon, 27 Apr 2026 16:46:29 +0100 Message-ID: <20260427154639.180684-15-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 600816914b46..a406b5ac120a 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -462,9 +462,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) @@ -476,6 +473,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 Wed Jun 17 06:03:54 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 4A34A3D75BA for ; Mon, 27 Apr 2026 15:48:03 +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=1777304884; cv=none; b=elRmCQba0m6rgqXlDbvyAFp64q2U8QMV96M1GN36+5Nc4TzNdOP/cWbu5LDygb3mVhLrBh20xoBB/lTZ1bQEmtE+kGvqOPC8anthLR233wnUJDGZ+oWOsXhOrzg69M3QlyXzSgeONztY7h/fGOske/AWs63eT3qUxEIiM4+jJ2M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304884; c=relaxed/simple; bh=5QREYDZ853cnryQHJ1yTlusLm/biu/XfvMWK4YdBhBs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WHLQiJGFgcREPUnX+6o1it5Du1w3sWDM9+1ciGyajsEFh7i7nIWp2LHrS/CECNb4SVtf7m+qF/Kl7Pm3MyCnAvh9Y0NwoTn432n4PS0BErDQkPn1kd2s76W/NaVAIApTqvzHHeO1j1/RlIMaEtxm7asM+gUSviveYgM67u8VLCI= 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=Fo07Y7u4; 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="Fo07Y7u4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304882; 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=HZzlSw2rTGIAd1QOD9s2kRLtq32SYiSJ31hk3o8bXSQ=; b=Fo07Y7u4/xPaEZmFKF+Jf6ahi3oX1kmDj4wUsAjr8gZGsYRQAMwTQWzkwfEfl6f3s8Ps3K v2Qd4WmInlOeE1VewJ3rYjI6c2Vh3nODEvfb9iPdzoIPs/bdJKP3QbEc49oXl99sVknhl8 WliWYaxTkaNLq0uK6syjtjdGmmTxTTc= 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-311-ca3hdlebOF6rWEa76BVDRA-1; Mon, 27 Apr 2026 11:47:56 -0400 X-MC-Unique: ca3hdlebOF6rWEa76BVDRA-1 X-Mimecast-MFC-AGG-ID: ca3hdlebOF6rWEa76BVDRA_1777304875 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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4EFF119560B0; Mon, 27 Apr 2026 15:47:55 +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 53946300070A; Mon, 27 Apr 2026 15:47:52 +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 v4 15/22] netfs: Fix leak of request in netfs_write_begin() error handling Date: Mon, 27 Apr 2026 16:46:30 +0100 Message-ID: <20260427154639.180684-16-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 a406b5ac120a..71ab81b3f609 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -692,9 +692,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 Wed Jun 17 06:03:54 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 2E6A634B1A7 for ; Mon, 27 Apr 2026 15:48:04 +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=1777304885; cv=none; b=aT2ggptdAJrN5gL1HP6nR57+YNF16NwCnSt6+kkxfAVQ6WcteTcnfo56fG1vniTiq2IBIVcwP12DHmxazGjFfiUXDsNQWvah4HXlCl6I3AJg6dgLHPjXepNZlQUugqfToa05nS70CEk8bVZpJnSA95DhFO/zKgHFpELb8/uQSaQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304885; c=relaxed/simple; bh=umEY6z5FzhVe9OfppsXVNGpeV9bgYr7l964IDKNxAUQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iByd6niTmjPH/8I4l8iGVJeuRXq7Tsh+ud8dgfJyLuz6HfLGRX2ismaZT2GP1OXsCr7JWGQzat2y4aUjbrtvBEOGkWMudyqG7f4UFPe3bnHWR6lfjf4/GsVy71yUuGMp4l4TU4bAVgZd/MEA2EFZzr9TiPugRfagw3QleZpkIoQ= 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=FlJ/TBJL; 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="FlJ/TBJL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304883; 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=zeNaf4Kz9zRtxWGIdVhluWxRyogCAQk408/i+Kq5848=; b=FlJ/TBJLw2q+TXuOY+wwSiIgZ0xISVAeDXc83XzcDEvYFUbed/5O8re5I29vTvvwNntsJU gX8kNZ6epHzmvjVJt1udVn8da8OEahxlLYiJYYTIvKq0K3vhJzz5DfnadeAjI4rt9mhabL eFYKvvs+1yryehcKCtleiiz1cX1903M= 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-321-Z6d3-rsPMmqEEZ6z9RTFzA-1; Mon, 27 Apr 2026 11:48:02 -0400 X-MC-Unique: Z6d3-rsPMmqEEZ6z9RTFzA-1 X-Mimecast-MFC-AGG-ID: Z6d3-rsPMmqEEZ6z9RTFzA_1777304880 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 4870419560BB; Mon, 27 Apr 2026 15:48:00 +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 078451800348; Mon, 27 Apr 2026 15:47:56 +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 v4 16/22] netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages() Date: Mon, 27 Apr 2026 16:46:31 +0100 Message-ID: <20260427154639.180684-17-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 71ab81b3f609..ec576d840594 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -675,7 +675,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); @@ -741,7 +741,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 f6b87a22c290..5847796b54ec 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 6e5e0da88290..92ed17a83c3d 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -290,7 +290,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 59f35d2eeb2e..eaf75543b64c 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 Wed Jun 17 06:03:54 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 7973C355F49 for ; Mon, 27 Apr 2026 15:48:10 +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=1777304891; cv=none; b=bLNLuE592dS/wxG3XNTI8K/QGXSitJy5fYYAstCJ3Az7WJCfD2bZIcDLS/yYetm9DDdYYLt5J7jwjHGSNkHNo++yaFPNpc02CXQnrMgZRv9i4G4tR4LYHixcjibL9VFGX2C4Dwc/Il80vt9KgTJEWb75MmC22fNHTu8uNrh5m3s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304891; c=relaxed/simple; bh=ZGvl6pwK8hhI6Z7/+zjDuc7j0IK6cFQmA5My4ZDgo6s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=U7IVsoUeXnZPL8HaLMp3KJoG0NUismD4fsnbKGP75/OgaqXrd1IvuUhciiNmtjmoKTRfLU10vO376ZcXZ804Dv0HEJinjJv+1rPIYaaCAQnxV1yyQrX4qY2vSJQ2iMu51NoGAl4vRnYcWv/aCMlsQLqaR5xGc7gOgGB6jkMbbUg= 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=RIQPWyeL; 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="RIQPWyeL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304889; 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=k8mwXPbUAnJI1Gm/W783qf7sgkpvXeGSln2ciNPV49w=; b=RIQPWyeLuxV1VZgEAp3ZqVolAqa2DiOhOA+uS81UrPBwn+CDiMcOfTtTEXWlyem6RZgjw/ urIobXrYHvJFQ/3AaRIzKcHQ+asckA2HdSgW5ihgxUl6nNcHLfwzDhpQZOQ2XXqfoF6lYx jx4rRsPLmEEGaIVrGwfwget5knul98M= 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-300-j5TdZ1OYN0mrz3ghpZriCA-1; Mon, 27 Apr 2026 11:48:06 -0400 X-MC-Unique: j5TdZ1OYN0mrz3ghpZriCA-1 X-Mimecast-MFC-AGG-ID: j5TdZ1OYN0mrz3ghpZriCA_1777304885 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 DB55919560B9; Mon, 27 Apr 2026 15:48:04 +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 DF56C19560AB; Mon, 27 Apr 2026 15:48:01 +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 v4 17/22] netfs: Fix potential uninitialised var in netfs_extract_user_iter() Date: Mon, 27 Apr 2026 16:46:32 +0100 Message-ID: <20260427154639.180684-18-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 adca78747f23..429e4396e1b0 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 Wed Jun 17 06:03:54 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 582133D9052 for ; Mon, 27 Apr 2026 15:48: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=1777304894; cv=none; b=t3aglq2EZ5pZ4nbNn3uxWk3C8+PFzTV/44ouQpwkEEvNfA6FR6PINrHUZXZkcaJN9Emhxc0NbqO8SzR3roSbWHec/OvIkBeFpmvl3uM2AchxGt1fTyS7eyO8Y3gYonCwMZrjwxrgObhw2mhvm0XeZMdLEXS6ZHl4PNFJhvwtckQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304894; c=relaxed/simple; bh=NWuEILu4xE6q9Mk9vp9plJM/HCqF87+ioNPLfDaVBUs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PZPEUQsnlicgF/cch8cWT6v8tbtet/hT4eLskniLAsj7gBnjN6CvhkIHYRvm5tPz9K0mYagWEnV+l6mFlmIWSXRlEVe9N+rqAlaAzrHNK76Naj12rQG8G6MwFbbGlHAoGSFwW3yWrwwueF/xPdo9EE/bNqvNT+gJR2OGD+sYiFo= 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=bJzdmRRB; 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="bJzdmRRB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304892; 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=Vdxi9UOZOtHgo5imTm/mIQ6/cjgNJUqbuly6giNT79Q=; b=bJzdmRRB+6fI2V1DxtLsn/Eh88Q+aq1GczL4Zyk5g8VWo9r1eFRVKHXxRc9ArIMpQLN1nB SCbvyvP3pfB4TYKFUKkDsBTeUBu28sONRGgYf57nQd2+o5tKFUcu3s6Iz8JppvR7ppvCTF h9aPy/yNS8gXxXzEgUzpScU5y7exJrY= 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-346-YVQkWbXNNeipfCuB7hO6uw-1; Mon, 27 Apr 2026 11:48:11 -0400 X-MC-Unique: YVQkWbXNNeipfCuB7hO6uw-1 X-Mimecast-MFC-AGG-ID: YVQkWbXNNeipfCuB7hO6uw_1777304889 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 807B71800473; Mon, 27 Apr 2026 15:48:09 +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 8E57A19560AB; Mon, 27 Apr 2026 15:48:06 +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 v4 18/22] netfs: Fix partial invalidation of streaming-write folio Date: Mon, 27 Apr 2026 16:46:33 +0100 Message-ID: <20260427154639.180684-19-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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" 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 4e91a8d75bce..094d5e8aad1a 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 Wed Jun 17 06:03:54 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 010EF34CFD0 for ; Mon, 27 Apr 2026 15:48:21 +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=1777304903; cv=none; b=iY5cDO6SR9AEw6s2T4Tj18hKmuEGKORHy1exWl1ygov5blv2tFEzNaUh7fE7YraG/+WtJqoy41fgVbhxjebLH8vhUnCS3w68NJ7CxQDrcD7gMtd16E6ntHMfL5jqVI7McgGhdBjZsEzQOTBGNj42aLKXDuhEl13o9oQeu8nGXAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304903; c=relaxed/simple; bh=XvI6FlmLo/S8fiCao0RLwRtkMMeM/s7ZEvhLOSP+1qU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=r2d3+m44/FCZ6zkpAAam5WqWW2pm2tgxctv8/Y8A1KSo5XV/K7bmrNI5HU+JvIBL4dUhbbeolr35woivEga2S7ujJdyAmJtGb2vb1wokwZVDO05/G8ysKpQ6HpF+Y97Y3ryuRNHrHTvefB4yUingWYALuk8DISOl5JAhWskscrE= 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=HaQYdF5/; 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="HaQYdF5/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304901; 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=diqcvgX9yY2xKRLwPEtaVSpqSiejOkpwkFqrTvXL1WI=; b=HaQYdF5/pUXkSOLRm8l+g/UvBuqcyk06+5jx9rRPZTzBT37t2aPc825bneOalu4PXPPeZ6 8IPSFf5C2mIJYXAR1oEGZdOpOxSe5383c7mpMy14mCBVcFC5NG9RdLfEIafxFquXhB6fXa 0PUXjflb9AajmZSrgKYy0VqbBR667H8= 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-672-kY-tnVtcNB66Q38Ojw__PQ-1; Mon, 27 Apr 2026 11:48:15 -0400 X-MC-Unique: kY-tnVtcNB66Q38Ojw__PQ-1 X-Mimecast-MFC-AGG-ID: kY-tnVtcNB66Q38Ojw__PQ_1777304894 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-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2A56B195608F; Mon, 27 Apr 2026 15:48:14 +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 3B4DF180045E; Mon, 27 Apr 2026 15:48:11 +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 v4 19/22] netfs: Fix folio->private handling in netfs_perform_write() Date: Mon, 27 Apr 2026 16:46:34 +0100 Message-ID: <20260427154639.180684-20-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 | 109 ++++++++++++++++++++--------------- include/trace/events/netfs.h | 1 + 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index b782cf83fb57..20ce8d04d874 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); @@ -572,7 +575,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 Wed Jun 17 06:03:54 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 978B23D9DCE for ; Mon, 27 Apr 2026 15:48:25 +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=1777304906; cv=none; b=JvaaS94lQvR8jIUbIwpCCg+Il0F6ANZamp7gFYo4uLQgM5Su3zV3tgsNpAuhKQgCT+CR5XD0fcFgdsM+UdcR6hK+c0jRo1w4fphk+r/DBJm3BvXjnCBWXOXIw7mycuNdvZw2UGxoWiyOgOErjQeIH04Wy5hBg0/NsKjQDkAD/2s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304906; c=relaxed/simple; bh=r6K9iFCwGOBbvUFsNm3usyWKYBGnIBFG0RK8j8V5wJE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nuWyUe7GDQr8M4yuUwFWlDucbgvaKhc2kboSoGsolqvJvNskKlvioCTkmKCcuYIePeQo7KvyyXdjVoGvHNnxyM8o94E+BZhhGX6Hr9R00JEar1l2+LoJMFXorDUaz0n36BkWgZKmzwJ8UHD3AcpIHSG61CGjpfGAYng5nONV9KU= 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=HNOEVu2s; 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="HNOEVu2s" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304904; 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=m8TreMojEQ0wPuyO6zSmrQCQs0j8kwVy3C2ZGai7LkE=; b=HNOEVu2sfnBfzLiaKh9qvY+Sx2hxgpgqKXw4nMIMpKJwykf3gEuaxzrAmBIzwzozxl57vE +7iJWD2aH1ydQITG2ZZCVpYp6/6mb9/E36qs1LMZaaeMduOrWvJiAMbBa3rsccplmVsa68 pi4v/IHUmAIdoi49IUfxzfF34cC9kfs= 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-140-ruZpO2nZNXevnvPOO4C5GA-1; Mon, 27 Apr 2026 11:48:21 -0400 X-MC-Unique: ruZpO2nZNXevnvPOO4C5GA-1 X-Mimecast-MFC-AGG-ID: ruZpO2nZNXevnvPOO4C5GA_1777304898 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 B94C3195608B; Mon, 27 Apr 2026 15:48:18 +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 BDD9719560AB; Mon, 27 Apr 2026 15:48:15 +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 v4 20/22] netfs: Fix netfs_read_folio() to wait on writeback Date: Mon, 27 Apr 2026 16:46:35 +0100 Message-ID: <20260427154639.180684-21-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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_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 ec576d840594..5e299237c9ec 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -508,6 +508,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 Wed Jun 17 06:03:54 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 6921B3DA5A0 for ; Mon, 27 Apr 2026 15:48:31 +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=1777304912; cv=none; b=S8fPR4Xlb4MpzVc5wtUtc5QgKjAtVRH/VGyCTLiYfelgWx85tKy4W9MkhrFMCgALHuOQkIyfUzBc3WSpmhz0/DGE8XMXswfUlLrZcbrcHZ6Vddh6xg6Mqvm6wjgKv9xn2ggkn2wC9LCAhRKTSf88tqW1zpkRWuR7XfBKWQqHows= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304912; c=relaxed/simple; bh=c5oDArXQJh7y60M1cLms/vcgi7TX/3O0SYtvfxSNmhk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AcQ/tBylThlFX39NNhc9PoTY8SkaXSLcgmpZjlb8BFmM2+6+sIWzIagwSHr6cxoRc0kYAjKrG36xGIb5NtNbs748+DlD0vEfZQ6F85/7dMwTj5AMsEB2nebnyO0m8PSzt9FNXXdHvGT1GYYFFM6rojXRa2xoM2sgRx52GuTrVPg= 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=MqeXaID7; 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="MqeXaID7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304910; 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=QS/xrleH5AOpAKSiASxQWMXbrc7cAq+mCszoPIawfWE=; b=MqeXaID7UlsDRDIs4KeWf1gh+XvRp5jgodjkDR8nVbq9dJThM/BgIdHbzLC8HPTDDKh9yu WALYqpHEc7btR0iVCEPR8+s6RylaJEzvOyLZtLZpGnLravANGviNb3foLUJAkmGHmjNLoq /oACKPw0zJQZ9AMJYsNMeDXJP6QqVrs= 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-61-BivbckB5P8qzrdjjQiZmnA-1; Mon, 27 Apr 2026 11:48:24 -0400 X-MC-Unique: BivbckB5P8qzrdjjQiZmnA-1 X-Mimecast-MFC-AGG-ID: BivbckB5P8qzrdjjQiZmnA_1777304903 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 5A28F19560AF; Mon, 27 Apr 2026 15:48:23 +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 6ADFB1800347; Mon, 27 Apr 2026 15:48:20 +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 v4 21/22] netfs, afs: Fix write skipping in dir/link writepages Date: Mon, 27 Apr 2026 16:46:36 +0100 Message-ID: <20260427154639.180684-22-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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. 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 | 8 +++++++- fs/netfs/write_issue.c | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index aaaa55878ffd..3379d01c72fb 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -2206,7 +2206,11 @@ 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) + return 0; + down_read(&dvnode->validate_lock); + } =20 if (is_dir ? test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : @@ -2214,6 +2218,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 b0e9690bb90c..60525cf6e679 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -819,6 +819,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, @@ -836,7 +839,7 @@ int netfs_writeback_single(struct address_space *mappin= g, if (!mutex_trylock(&ictx->wb_lock)) { if (wbc->sync_mode =3D=3D WB_SYNC_NONE) { 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 Wed Jun 17 06:03:54 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 200A23DA7DA for ; Mon, 27 Apr 2026 15:48: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=1777304916; cv=none; b=GeTtA1WxsQt6pDDDqzPjCLJiTlQCuX/edjuoH4R8IYzgCvevkSJ5WFwxdqUE9vJxeDSnhWpb2ohl0/C6NTtAVhzd83WYuZoQLJzTFsO4vNxD6EsRy7a3eMtkF37LYWcMNP6q/ww0B0Y1jVe7IVAU930QvZqry2o02MjHbmudE/U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777304916; c=relaxed/simple; bh=we/DZ7XS7ThpZConRkIb81ZLIEbmo6XC0mu3XuzZCvs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j8VKXc5DBJg9k9g8mxdVrLbf/pjVwtJNp55wD9UWZ5IwlKx0tSBxwMa2GAaOp8A+M42KInOaPM6Vuf0x4UwrtWXGbgZP/LtEB41yR78qNXpSII977NY03QiG2pNqGps/EaQcmRSpEaedkVqdzKjdQJsA+3TA7YUlrK30Z3vLBTI= 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=YRKRdjP7; 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="YRKRdjP7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777304913; 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=MO61n1x3exEVh2twI4/GkaiLmVRA4RZFNjdjnieVLGM=; b=YRKRdjP7rKfmzpi9p3TzUqK7nn2dTXMKoTjh+Z4wJSM+eDR81EoaZeMdL7jR/xiJ7keL7p CXLs5CGPXwikOcIb4JMw42gWhZ1LYBr4Es4fs1RH2nyYl6kaaw97WosopbFJ9zjKGdl2L+ LOzvxn4KRg7A+JhIoRBZrEioNQ6rh+w= 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-507-4DqsPIZzPS-rAJkKIAXW4A-1; Mon, 27 Apr 2026 11:48:29 -0400 X-MC-Unique: 4DqsPIZzPS-rAJkKIAXW4A-1 X-Mimecast-MFC-AGG-ID: 4DqsPIZzPS-rAJkKIAXW4A_1777304908 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 5757C1800454; Mon, 27 Apr 2026 15:48:28 +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 0FEDF19560AB; Mon, 27 Apr 2026 15:48:24 +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 v4 22/22] afs: Fix the locking used by afs_get_link() Date: Mon, 27 Apr 2026 16:46:37 +0100 Message-ID: <20260427154639.180684-23-dhowells@redhat.com> In-Reply-To: <20260427154639.180684-1-dhowells@redhat.com> References: <20260427154639.180684-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 | 267 ++++++++++++++++++++++++++++++++++++++++++++ fs/afs/validation.c | 8 +- fs/afs/yfsclient.c | 4 +- 8 files changed, 342 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 3379d01c72fb..87e90b8560e6 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 @@ -2212,9 +2214,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..242829121e01 --- /dev/null +++ b/fs/afs/symlink.c @@ -0,0 +1,267 @@ +// 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) + 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);