All of lore.kernel.org
 help / color / mirror / Atom feed
From: Heming Zhao <heming.zhao@suse.com>
To: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: mark@fasheh.com, jlbec@evilplan.org, hch@lst.de,
	 ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v2 3/4] ocfs2: switch dio write path from buffer_head to iomap
Date: Sun, 2 Aug 2026 18:37:27 +0800	[thread overview]
Message-ID: <am79Sf7c1TMxzAom@p15> (raw)
In-Reply-To: <b4942947-86cd-45d1-8f31-c8f55e0ae71d@linux.alibaba.com>

On Tue, Jul 28, 2026 at 02:07:42PM +0800, Joseph Qi wrote:
> 
> 
> On 7/27/26 2:17 PM, Heming Zhao wrote:
> > This patch converts OCFS2's DIO write path from the legacy
> > buffer_head infrastructure to the modern iomap framework.
> > 
> > Key modifications and designs are as follows:
> > 
> > 1. Dynamic Context Allocation:
> >    Refactor 'struct ocfs2_write_ctxt' to use a flexible array 'w_desc[]'
> >    instead of a fixed-size array. Dynamically allocate the context based on
> >    the write length ('w_clen') in 'ocfs2_alloc_write_ctxt()'. This prevents
> >    static limits overflow and optimizes kernel heap memory utilization.
> > 
> > 2. Robust Mapping and Limits:
> >    Introduce 'ocfs2_dio_wr_map_blocks()' to allocate and map direct write
> >    blocks. Implement a 1 MiB cap ('OCFS2_DIO_WR_MAX_MAX_BYTES') per mapping
> >    call to restrict allocation granularity, preventing JBD2 transaction
> >    credit exhaustion during huge asynchronous sequential writes.
> > 
> > 3. Reliable Completion Work and Fallback:
> >    - Implement 'ocfs2_iomap_dio_end_io_write()' to handle metadata completion.
> >      It converts UNWRITTEN extents, updates inode size (EOF), and deletes the
> >      inode from the orphan directory if it was appended.
> >    - Implement 'ocfs2_dio_write_end_io()' to finalize the dio lifecycle and
> >      release cluster locks safely.
> >    - Intercept '-ENOTBLK' errors from 'iomap_dio_rw()' caused by page cache
> >      invalidation failures (due to mmap/buffered collisions). Gracefully clear
> >      the error, strip the IOCB_DIRECT flag, and fall back to buffered write
> > 
> > 4. Moved ocfs2_add_inode_to_orphan():
> >    - moved ocfs2_add_inode_to_orphan() from ocfs2_dio_wr_map_blocks() to
> >      ocfs2_file_write_iter().
> > 
> > 5. Uncertain logic in code
> >    For the following code block in ocfs2_dio_wr_map_blocks():
> >    ```
> >      if (extend) {
> >              if (ocfs2_sparse_alloc(osb))
> >                      ret = ocfs2_zero_tail(inode, di_bh, pos);
> >              else
> >                      ret = ocfs2_expand_nonsparse_inode(inode, di_bh, pos,
> >                                                          map_len, NULL);
> >              if (ret < 0) {
> >                      mlog_errno(ret);
> >                      goto unlock;
> >              }
> >      }
> >    ```
> >    I am not completely certain whether it is called only once per
> >    ocfs2_file_write_iter(), but I believe calling it multiple times will
> >    not introduce any side effects. Furthermore, testing across various
> >    scenarios showed no instances of multiple calls.
> > 
> > Assisted-by: Gemini:gemini-3.5-flash
> > Assisted-by: Claude:claude-sonnet-4-5
> > Co-developed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> > Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> > Signed-off-by: Heming Zhao <heming.zhao@suse.com>
> > ---
> >  fs/ocfs2/aops.c           | 394 ++++++++++++++++++++++++++++++++++++--
> >  fs/ocfs2/buffer_head_io.c |   7 +-
> >  fs/ocfs2/file.c           |  91 +++++++--
> >  fs/ocfs2/ocfs2.h          |   2 +
> >  4 files changed, 459 insertions(+), 35 deletions(-)
> > 
> > diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
> > index 12f5f2e3530a..9a079436c9c0 100644
> > --- a/fs/ocfs2/aops.c
> > +++ b/fs/ocfs2/aops.c
> 
> ...
> 
> > +
> > +	ocfs2_free_unwritten_list(inode, &wc->w_unwritten_list);
> > +	ret = ocfs2_write_end_nolock(inode->i_mapping, pos, map_len, map_len, wc);
> > +	BUG_ON(ret != map_len);
> 
> Under memory pressure, folio allocation may fail. In this case,
> ocfs2_write_end_nolock() can return a short count.
> So we must handle this case gracefully.

I simply copied the code logic from ocfs2_dio_wr_get_block().

IIUC, folio allocation mainly happens in ocfs2_write_begin_nolock =>
ocfs2_grab_pages_for_write. while ocfs2_write_end_nolock is responsible for
marking pages dirty.
If the code encounters memory pressure issue, the existing error handling should
be enough (I mean the error handling after ocfs2_write_begin_nolock()).

> 
> > +	ret = 0;
> > +
> > +unlock:
> > +	up_write(&oi->ip_alloc_sem);
> > +	ocfs2_inode_unlock(inode, 1);
> > +	brelse(di_bh);
> > +
> > +out:
> > +	return ret;
> > +}
> > +
> >  static int ocfs2_dio_end_io_write(struct inode *inode,
> >  				  struct ocfs2_dio_write_ctxt *dwc,
> >  				  loff_t offset,
> 
> ...
> 
> > +static int ocfs2_iomap_dio_end_io_write(struct inode *inode,
> > +				  loff_t offset,
> > +				  ssize_t bytes)
> > +{
> > +	struct ocfs2_cached_dealloc_ctxt dealloc;
> > +	struct ocfs2_extent_tree et;
> > +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> > +	struct ocfs2_inode_info *oi = OCFS2_I(inode);
> > +	struct buffer_head *di_bh = NULL;
> > +	struct ocfs2_dinode *di;
> > +	struct ocfs2_alloc_context *data_ac = NULL;
> > +	struct ocfs2_alloc_context *meta_ac = NULL;
> > +	handle_t *handle = NULL;
> > +	loff_t end = offset + bytes;
> > +	int ret = 0, credits = 0;
> > +	struct ocfs2_map_block map;
> > +	unsigned int blkbits = inode->i_blkbits;
> > +	unsigned int max_blocks;
> > +	unsigned int ue_cpos = 0, ue_phys = 0, ue_len = 0;
> > +	unsigned int curr_lblk, end_lblk;
> > +
> > +	map.lblk = offset >> blkbits;
> > +	max_blocks = (bytes + offset) >> osb->s_clustersize_bits;
> 
> Seems unused.
> 

Yes.
> ...
> 
> > +	curr_lblk = offset >> blkbits;
> > +	/*
> > +	 * Round the end up so the final partial block (sub-block direct I/O)
> > +	 * is included; otherwise the last, partially-written cluster is left
> > +	 * unwritten and reads back as zero.
> > +	 */
> > +	end_lblk = (offset + bytes + (1 << blkbits) - 1) >> blkbits;
> > +	while (ret >= 0 && curr_lblk < end_lblk) {
> > +		memset(&map, 0, sizeof(map));
> > +		map.lblk += curr_lblk;
> 
> Since map is memset just now, so here we can use "map.lblk = curr_lblk"
> directly.

Agree.
> 
> > +		map.len = end_lblk - curr_lblk;
> > +
> > +		ret = ocfs2_assure_trans_credits(handle, credits);
> > +		if (ret < 0) {
> > +			mlog_errno(ret);
> > +			break;
> > +		}
> > +
> 
> ...
> 
> > +
> > +static int ocfs2_dio_write_end_io(struct kiocb *iocb, ssize_t size,
> > +			int error, unsigned int flags, int level)
> > +{
> > +	struct inode *inode = file_inode(iocb->ki_filp);
> > +	loff_t offset = iocb->ki_pos;
> > +	int ret = 0;
> > +
> > +	if (error)
> > +		mlog_ratelimited(ML_ERROR, "Direct IO failed, bytes = %lld errno:%d",
> > +				 (long long)size, error);
> > +
> > +	if (size && ((flags & IOMAP_DIO_UNWRITTEN) ||
> > +		    (offset + size > i_size_read(inode)))) {
> 
> Is it safe to do i_size_read() in case async dio completion?
> 

I set IOMAP_DIO_FORCE_WAIT in ocfs2_file_write_iter() when the inode extends the
i_size. Without IOMAP_DIO_FORCE_WAIT, xfstests will fail in many cases.

Thanks,
Heming

  reply	other threads:[~2026-08-02 10:37 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-27  6:17 [RFC PATCH v2 0/4] migrates ocfs2 DIO from buffer_head to iomap Heming Zhao
2026-07-27  6:17 ` [RFC PATCH v2 1/4] ocfs2: Add new ocfs2_map_blocks() to introduce iomap feature Heming Zhao
2026-07-28  5:41   ` Joseph Qi
2026-08-02 10:17     ` Heming Zhao
2026-08-02 11:28     ` Heming Zhao
2026-07-27  6:17 ` [RFC PATCH v2 2/4] ocfs2: switch dio read path from buffer_head to iomap Heming Zhao
2026-07-28  4:16   ` Christoph Hellwig
2026-08-02 10:30     ` Heming Zhao
2026-07-28  5:50   ` Joseph Qi
2026-08-02 10:36     ` Heming Zhao
2026-07-27  6:17 ` [RFC PATCH v2 3/4] ocfs2: switch dio write " Heming Zhao
2026-07-28  4:18   ` Christoph Hellwig
2026-07-28  6:07   ` Joseph Qi
2026-08-02 10:37     ` Heming Zhao [this message]
2026-07-27  6:18 ` [RFC PATCH v2 4/4] ocfs2: remove legacy blockdev direct-IO path APIs Heming Zhao
2026-07-28  4:19   ` Christoph Hellwig
2026-07-28  4:25     ` Heming Zhao
2026-07-28  6:11   ` Joseph Qi
2026-08-02 10:37     ` Heming Zhao
2026-07-28  4:11 ` [RFC PATCH v2 0/4] migrates ocfs2 DIO from buffer_head to iomap Christoph Hellwig

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=am79Sf7c1TMxzAom@p15 \
    --to=heming.zhao@suse.com \
    --cc=hch@lst.de \
    --cc=jlbec@evilplan.org \
    --cc=joseph.qi@linux.alibaba.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark@fasheh.com \
    --cc=ocfs2-devel@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.