From: Joseph Qi <joseph.qi@linux.alibaba.com>
To: Heming Zhao <heming.zhao@suse.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: Tue, 28 Jul 2026 14:07:42 +0800 [thread overview]
Message-ID: <b4942947-86cd-45d1-8f31-c8f55e0ae71d@linux.alibaba.com> (raw)
In-Reply-To: <20260727061802.18485-4-heming.zhao@suse.com>
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.
> + 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.
...
> + 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.
> + 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?
...
next prev parent reply other threads:[~2026-07-28 6:07 UTC|newest]
Thread overview: 14+ 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-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-07-28 5:50 ` Joseph Qi
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 [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-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=b4942947-86cd-45d1-8f31-c8f55e0ae71d@linux.alibaba.com \
--to=joseph.qi@linux.alibaba.com \
--cc=hch@lst.de \
--cc=heming.zhao@suse.com \
--cc=jlbec@evilplan.org \
--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.