Linux 9p file system development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "David Howells" <dhowells@redhat.com>
Cc: v9fs@lists.linux.dev, "Chuck Lever" <cel@kernel.org>,
	"Jeff Layton" <jlayton@kernel.org>,
	"Anna Schumaker" <anna@kernel.org>
Subject: Re: [PATCH v16 07/15] netfs: Explicitly pass tail padding to netfs_writeback_single()
Date: Sat, 07 Nov 2026 17:46:10 +0000	[thread overview]
Message-ID: <sashiko-outbox-203129@kernel.org> (raw)
In-Reply-To: <20261107160823.1465553-8-dhowells@redhat.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] afs: race on i_size in afs_dir_writepages() triggers
  WARN_ON_ONCE()
- [Medium] netfs: netfs_writeback_single() does not set wreq->i_size to
  len
- [Medium] afs: i_size exceeding directory_size in writepages triggers
  WARN_ON_ONCE()
--

commit 85f14f8d404627b37bc4c8b65ec7be7797cc5f30
Author: David Howells <dhowells@redhat.com>

netfs: Explicitly pass tail padding to netfs_writeback_single()

This commit changes netfs_writeback_single() to take an explicit content
length alongside the full DIO-rounded buffer iterator, and updates AFS
directory and symlink writeback to zero the buffer tail and pass both
lengths.

> diff --git a/fs/afs/dir.c b/fs/afs/dir.c
> index 75f8f70cfeff0..51c158d8f723f 100644
> --- a/fs/afs/dir.c
> +++ b/fs/afs/dir.c

[ ... ]

> @@ -2228,9 +2236,10 @@ static int afs_dir_writepages(struct address_space *mapping,
>  	}
>  
>  	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 = netfs_writeback_single(mapping, wbc, &iter);
> +		iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory,
> +				     0, 0, dvnode->directory_size);
> +		ret = netfs_writeback_single(mapping, wbc, &iter,
> +					     i_size_read(&dvnode->netfs.inode));
>  		if (ret == 1)
>  			ret = 0; /* Skipped write due to lock conflict. */
>  	}

[Severity: Medium]
Can a concurrent status update grow inode->i_size past
dvnode->directory_size here and trigger the WARN_ON_ONCE() in
netfs_writeback_single()?

RPC completion callbacks such as afs_do_lookup_success() call
afs_vnode_commit_status() -> afs_apply_status() without holding
dvnode->validate_lock:

CPU0                                  CPU1
afs_dir_writepages()
  down_read(&dvnode->validate_lock)
  test_bit(AFS_VNODE_DIR_VALID, ...)
  iov_iter_folio_queue(...,
    dvnode->directory_size)
                                      afs_do_lookup_success()
                                        ...
                                        afs_apply_status()
                                          afs_invalidate_dir()
                                          netfs_write_sizes()
  i_size_read(&dvnode->netfs.inode)
  netfs_writeback_single()
    WARN_ON_ONCE(iov_iter_count(iter) < len)  <-- triggers warning, -EIO

If a remote modification grows the directory beyond dvnode->directory_size
in that window, netfs_writeback_single() warns and fails writeback with
-EIO.

> diff --git a/fs/afs/symlink.c b/fs/afs/symlink.c
> index a1dbd552ddff0..c18e4f8064234 100644
> --- a/fs/afs/symlink.c
> +++ b/fs/afs/symlink.c
> @@ -228,6 +228,10 @@ int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
>  /*
>   * Write the symlink contents to the cache as a single blob.  We then throw
>   * away the page we used to receive it.
> + *
> + * Note that the buffer tail is cleared under a shared lock on ->validate_lock
> + * - though hopefully there won't be racing writers since the only
> + * 'modification' is to be either read from the server or locally created.
>   */
>  int afs_symlink_writepages(struct address_space *mapping,
>  			   struct writeback_control *wbc)
> @@ -247,9 +251,17 @@ int afs_symlink_writepages(struct address_space *mapping,
>  
>  	if (vnode->directory &&
>  	    atomic64_read(&vnode->cb_expires_at) != AFS_NO_CB_PROMISE) {
> +		size_t i_size = i_size_read(&vnode->netfs.inode);
> +
> +		if (i_size < vnode->directory_size) {
> +			iov_iter_folio_queue(&iter, ITER_DEST, vnode->directory, 0, 0,
> +					     vnode->directory_size);
> +			iov_iter_advance(&iter, i_size);
> +			iov_iter_zero(vnode->directory_size - i_size, &iter);
> +		}
>  		iov_iter_folio_queue(&iter, ITER_SOURCE, vnode->directory, 0, 0,
> -				     i_size_read(&vnode->netfs.inode));
> -		ret = netfs_writeback_single(mapping, wbc, &iter);
> +				     vnode->directory_size);
> +		ret = netfs_writeback_single(mapping, wbc, &iter, i_size);
>  	}

[Severity: Medium]
Can i_size exceed vnode->directory_size in afs_symlink_writepages() and
trigger WARN_ON_ONCE(iov_iter_count(iter) < len) in
netfs_writeback_single()?

In afs_do_read_symlink(), vnode->directory is allocated to PAGE_SIZE.  If
netfs_read_single() fetches the symlink from the server and the returned
AFSFetchStatus reports status.size > PAGE_SIZE, afs_apply_status() updates
inode->i_size to status.size and sets the callback promise while
netfs_rreq_assess_single() marks the inode dirty.  afs_do_read_symlink()
then returns -EFBIG without freeing vnode->directory or clearing the dirty
state.  When afs_symlink_writepages() later runs, i_size is larger than
vnode->directory_size, causing netfs_writeback_single() to warn and return
-EIO (which also skips freeing vnode->directory).

> diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
> index b8237e1314d8a..e041023811ed0 100644
> --- a/fs/netfs/write_issue.c
> +++ b/fs/netfs/write_issue.c
> @@ -747,26 +747,33 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
>   * netfs_writeback_single - Write back a monolithic payload
>   * @mapping: The mapping to write from
>   * @wbc: Hints from the VM
> - * @iter: Data to write, must be ITER_FOLIOQ.
> + * @iter: Data buffer to write from, must be ITER_FOLIOQ.
> + * @len: Amount of data to write.
>   *
>   * Write a monolithic, non-pagecache object back to the server and/or
>   * the cache.
>   *
> + * The data buffer may be bigger than @len (as set in @iter's count), and if
> + * so, this function will use the extra as needed to provide padding into the
> + * cache to handle DIO rounding, so the caller should clear it.
> + *
>   * 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,
> -			   struct iov_iter *iter)
> +			   struct iov_iter *iter, size_t len)
>  {
>  	struct netfs_io_request *wreq;
>  	struct netfs_inode *ictx = netfs_inode(mapping->host);
>  	struct folio_queue *fq;
> -	size_t size = iov_iter_count(iter);
> +	size_t size = len;
>  	int ret;
>  
>  	if (WARN_ON_ONCE(!iov_iter_is_folioq(iter)))
>  		return -EIO;
> +	if (WARN_ON_ONCE(iov_iter_count(iter) < len))
> +		return -EIO;
>  
>  	if (!netfs_wb_begin(ictx, wbc->sync_mode == WB_SYNC_NONE)) {
>  		/* The VFS will have undirtied the inode. */

[Severity: Medium]
Should netfs_writeback_single() initialize wreq->i_size from len instead of
letting netfs_create_write_req() -> netfs_alloc_request() re-read
i_size_read(inode)?

If inode->i_size changes concurrently (via afs_apply_status() ->
netfs_write_sizes()) between the caller passing len and
netfs_alloc_request() reading wreq->i_size, wreq->i_size and len disagree.
If wreq->i_size < len and the loop advances to a folio with fpos >
wreq->i_size, netfs_write_folio_single() underflows the unsigned
wreq->i_size - fpos subtraction:

fs/netfs/write_issue.c:netfs_write_folio_single() {
    ...
	flen = folio_size(folio);
	if (flen > wreq->i_size - fpos) {
		flen = wreq->i_size - fpos;
		folio_zero_segment(folio, flen, fsize);
		to_eof = true;
	} else if (flen == wreq->i_size - fpos) {
		to_eof = true;
	}
    ...
}

Although the follow-up commit "netfs: Change netfs_writeback_single() to
not look inside the iterator" removes netfs_write_folio_single() and sets
wreq->len = len, it still leaves wreq->i_size initialized from
i_size_read(inode), which causes cachefiles_issue_write() to skip DIO tail
padding when start + len != wreq->i_size and triggers WARN_ON(len &
(cache->bsize - 1)) in cachefiles_collect_write().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107160823.1465553-1-dhowells@redhat.com?part=7

  reply	other threads:[~2026-11-07 17:46 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-07 16:08 [PATCH v16 00/15] netfs, iov_iter: Use a chain of bio_vec arrays instead of folio_queue David Howells
2026-11-07 16:08 ` [PATCH v16 01/15] Add a function to kmap one page of a multipage bio_vec David Howells
2026-11-07 16:15   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 02/15] iov_iter: Add a segmented queue of bio_vec[] David Howells
2026-11-07 16:31   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 03/15] netfs: Add some tools for managing bvecq chains David Howells
2026-11-07 16:46   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 04/15] iov_iter: Add an API document for the bvecq structure David Howells
2026-11-07 16:47   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 05/15] iov_iter: Add a bvecq kunit test David Howells
2026-11-07 17:01   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 06/15] afs: Fix an inconsistency in the symlink code David Howells
2026-11-07 17:09   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 07/15] netfs: Explicitly pass tail padding to netfs_writeback_single() David Howells
2026-11-07 17:46   ` sashiko-bot [this message]
2026-11-07 16:08 ` [PATCH v16 08/15] netfs: Change netfs_writeback_single() to not look inside the iterator David Howells
2026-11-07 18:09   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 09/15] afs: Use a bvecq to hold dir content rather than folioq David Howells
2026-11-07 18:34   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 10/15] cifs: Use a bvecq for buffering instead of a folioq David Howells
2026-11-07 18:51   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 11/15] smbdirect: Support ITER_BVECQ in smbdirect_map_sges_from_iter() David Howells
2026-11-07 18:57   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 12/15] netfs: Switch folioq to bvecq David Howells
2026-11-07 19:25   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 13/15] smbdirect: Remove support for ITER_FOLIOQ from smbdirect_map_sges_from_iter() David Howells
2026-11-07 19:29   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 14/15] iov_iter: Remove ITER_FOLIOQ David Howells
2026-11-07 19:31   ` sashiko-bot
2026-11-07 16:08 ` [PATCH v16 15/15] netfs: Remove folio_queue David Howells
2026-11-07 19:35   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-203129@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=anna@kernel.org \
    --cc=cel@kernel.org \
    --cc=dhowells@redhat.com \
    --cc=jlayton@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=v9fs@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox