| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (c) 2016-2026 Christoph Hellwig. | |
| * All Rights Reserved. | |
| */ | |
| #include "xfs_platform.h" | |
| #include "xfs_shared.h" | |
| #include "xfs_format.h" | |
| #include "xfs_log_format.h" | |
| #include "xfs_trans_resv.h" | |
| #include "xfs_mount.h" | |
| #include "xfs_inode.h" | |
| #include "xfs_iomap.h" | |
| #include "xfs_trace.h" | |
| #include "xfs_bmap_util.h" | |
| #include "xfs_reflink.h" | |
| #include "xfs_zone_alloc.h" | |
| #include "xfs_ioend.h" | |
| #include "xfs_error.h" | |
| #include "xfs_errortag.h" | |
| #include <linux/bio-integrity.h> | |
| static void | |
| xfs_dio_bounce_end_io( | |
| struct bio *bio) | |
| { | |
| struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); | |
| int error = blk_status_to_errno(bio->bi_status); | |
| struct bio *orig_bio = bio->bi_private; | |
| if ((ioend->io_flags & IOMAP_IOEND_INTEGRITY) && !bio->bi_status) | |
| error = iomap_ioend_integrity_verify(ioend); | |
| iomap_bounce_read_end_io(ioend, orig_bio, error); | |
| } | |
| static void | |
| xfs_bounce_submit_ioend( | |
| struct iomap_ioend *ioend) | |
| { | |
| if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) | |
| fs_bio_integrity_alloc(&ioend->io_bio); | |
| ioend->io_bio.bi_end_io = xfs_dio_bounce_end_io; | |
| bio_set_flag(&ioend->io_bio, BIO_COMPLETE_IN_TASK); | |
| submit_bio(&ioend->io_bio); | |
| } | |
| static void | |
| xfs_end_bio_bounced( | |
| struct bio *bio) | |
| { | |
| /* | |
| * Just complete the original ioends as all verification is done by the | |
| * end_io handlers for the clone bio(s). | |
| */ | |
| iomap_finish_ioends(iomap_ioend_from_bio(bio), | |
| blk_status_to_errno(bio->bi_status)); | |
| } | |
| static void | |
| xfs_read_bounce_and_resubmit( | |
| struct iomap_ioend *ioend) | |
| { | |
| struct bio *bio = &ioend->io_bio; | |
| struct xfs_inode *ip = XFS_I(ioend->io_inode); | |
| unsigned int nofs_flag = memalloc_nofs_save(); | |
| trace_xfs_bounce_reread(ip, ioend->io_offset, ioend->io_size); | |
| /* | |
| * Free the bio integrity data for the original bio, as we'll allocate | |
| * a new one for each sub-I/O, which could deadlock if we keep the | |
| * integrity data for the original bio around. | |
| */ | |
| if (bio_integrity(bio)) | |
| fs_bio_integrity_free(bio); | |
| /* | |
| * Resubmit the bio through the iomap bounce machinery. The original | |
| * bio itself is not resubmitted to the block layer, but just used to | |
| * track I/O completion of the cloned bios. | |
| */ | |
| bio_prepare_reissue(bio, xfs_inode_buftarg(ip)->bt_bdev); | |
| bio->bi_iter = (struct bvec_iter) { | |
| .bi_sector = ioend->io_sector, | |
| .bi_size = ioend->io_size, | |
| .bi_offset = ioend->io_bvec_offset, | |
| }; | |
| bio->bi_end_io = xfs_end_bio_bounced; | |
| iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev), | |
| xfs_bounce_submit_ioend); | |
| memalloc_nofs_restore(nofs_flag); | |
| } | |
| static void | |
| xfs_end_io_read( | |
| struct bio *bio) | |
| { | |
| struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); | |
| struct xfs_inode *ip = XFS_I(ioend->io_inode); | |
| struct xfs_mount *mp = ip->i_mount; | |
| int error = blk_status_to_errno(bio->bi_status); | |
| if (!error && (ioend->io_flags & IOMAP_IOEND_INTEGRITY)) { | |
| error = iomap_ioend_integrity_verify(ioend); | |
| if ((ioend->io_flags & IOMAP_IOEND_DIRECT) && | |
| READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_LAZY) { | |
| /* | |
| * We only really need to retry for guard tag errors, | |
| * but right now we can't distinguish them from other | |
| * (i.e, reftag) errors. | |
| */ | |
| if (error || | |
| XFS_TEST_ERROR(mp, XFS_ERRTAG_BOUNCE_REREAD)) { | |
| xfs_read_bounce_and_resubmit(ioend); | |
| return; | |
| } | |
| } | |
| } | |
| iomap_finish_ioends(ioend, error); | |
| } | |
| void | |
| xfs_ioend_submit_read( | |
| struct inode *inode, | |
| struct bio *bio, | |
| loff_t file_offset, | |
| u16 ioend_flags) | |
| { | |
| struct xfs_inode *ip = XFS_I(inode); | |
| struct xfs_mount *mp = ip->i_mount; | |
| struct iomap_ioend *ioend; | |
| ioend = iomap_init_ioend(inode, bio, file_offset, ioend_flags); | |
| if ((ioend_flags & IOMAP_IOEND_DIRECT) && | |
| READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_ALWAYS) { | |
| iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev), | |
| xfs_bounce_submit_ioend); | |
| return; | |
| } | |
| if (ioend_flags & IOMAP_IOEND_INTEGRITY) | |
| fs_bio_integrity_alloc(bio); | |
| bio->bi_end_io = xfs_end_io_read; | |
| bio_set_flag(bio, BIO_COMPLETE_IN_TASK); | |
| submit_bio(bio); | |
| } | |
| static void | |
| xfs_end_ioend_write_zoned( | |
| struct iomap_ioend *ioend) | |
| { | |
| struct xfs_inode *ip = XFS_I(ioend->io_inode); | |
| struct xfs_open_zone *oz = ioend->io_private; | |
| struct iomap_ioend *tmp; | |
| int error = -EIO; | |
| ASSERT(!(ioend->io_flags & IOMAP_IOEND_SHARED)); | |
| if (xfs_is_shutdown(ip->i_mount)) | |
| goto done; | |
| /* | |
| * Zoned writes update the in-core open zone accounting before I/O | |
| * submission. A failed write leaves that state inconsistent, so shut | |
| * down the filesystem instead of letting later writers wait forever for | |
| * open zone space to become available. | |
| */ | |
| error = blk_status_to_errno(ioend->io_bio.bi_status); | |
| if (unlikely(error)) { | |
| xfs_force_shutdown(ip->i_mount, SHUTDOWN_META_IO_ERROR); | |
| goto done; | |
| } | |
| error = xfs_zoned_end_io(ip, ioend->io_offset, ioend->io_size, | |
| ioend->io_sector, oz, NULLFSBLOCK); | |
| if (error) | |
| goto done; | |
| if (!(ioend->io_flags & IOMAP_IOEND_DIRECT) && | |
| xfs_ioend_is_append(ioend)) | |
| error = xfs_setfilesize(ip, ioend->io_offset, ioend->io_size); | |
| done: | |
| /* | |
| * Put the open zone references for all ioends merged into this one in | |
| * addition to the main one they were merged into. If the submission | |
| * failed before xfs_zone_alloc_and_submit got called, there is no open | |
| * zone and at least in theory that could also happen for merged ioends. | |
| */ | |
| list_for_each_entry(tmp, &ioend->io_list, io_list) { | |
| if (tmp->io_private) | |
| xfs_open_zone_put(tmp->io_private); | |
| } | |
| if (oz) | |
| xfs_open_zone_put(oz); | |
| iomap_finish_ioends(ioend, error); | |
| } | |
| static void | |
| xfs_end_ioend_write( | |
| struct iomap_ioend *ioend) | |
| { | |
| struct xfs_inode *ip = XFS_I(ioend->io_inode); | |
| struct xfs_mount *mp = ip->i_mount; | |
| xfs_off_t offset = ioend->io_offset; | |
| size_t size = ioend->io_size; | |
| int error; | |
| /* | |
| * Just clean up the in-memory structures if the fs has been shut down. | |
| */ | |
| if (xfs_is_shutdown(mp)) { | |
| error = -EIO; | |
| goto done; | |
| } | |
| /* | |
| * Clean up all COW blocks and underlying data fork delalloc blocks on | |
| * I/O error. The delalloc punch is required because this ioend was | |
| * mapped to blocks in the COW fork and the associated pages are no | |
| * longer dirty. If we don't remove delalloc blocks here, they become | |
| * stale and can corrupt free space accounting on unmount. | |
| */ | |
| error = blk_status_to_errno(ioend->io_bio.bi_status); | |
| if (unlikely(error)) { | |
| if (ioend->io_flags & IOMAP_IOEND_SHARED) { | |
| xfs_reflink_cancel_cow_range(ip, offset, size, true); | |
| xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset, | |
| offset + size, NULL); | |
| } | |
| goto done; | |
| } | |
| /* | |
| * Success: commit the COW or unwritten blocks if needed. | |
| */ | |
| if (ioend->io_flags & IOMAP_IOEND_SHARED) | |
| error = xfs_reflink_end_cow(ip, offset, size); | |
| else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN) | |
| error = xfs_iomap_write_unwritten(ip, offset, size, false); | |
| if (!error && | |
| !(ioend->io_flags & IOMAP_IOEND_DIRECT) && | |
| xfs_ioend_is_append(ioend)) | |
| error = xfs_setfilesize(ip, offset, size); | |
| done: | |
| iomap_finish_ioends(ioend, error); | |
| } | |
| /* | |
| * Finish all pending IO completions that require transactional modifications. | |
| * | |
| * We try to merge physical and logically contiguous ioends before completion to | |
| * minimise the number of transactions we need to perform during IO completion. | |
| * Both unwritten extent conversion and COW remapping need to iterate and modify | |
| * one physical extent at a time, so we gain nothing by merging physically | |
| * discontiguous extents here. | |
| * | |
| * The ioend chain length that we can be processing here is largely unbound in | |
| * length and we may have to perform significant amounts of work on each ioend | |
| * to complete it. Hence we have to be careful about holding the CPU for too | |
| * long in this loop. | |
| */ | |
| void | |
| xfs_end_io( | |
| struct work_struct *work) | |
| { | |
| struct xfs_inode *ip = | |
| container_of(work, struct xfs_inode, i_ioend_work); | |
| struct iomap_ioend *ioend; | |
| struct list_head tmp; | |
| unsigned int nofs_flag; | |
| unsigned long flags; | |
| spin_lock_irqsave(&ip->i_ioend_lock, flags); | |
| list_replace_init(&ip->i_ioend_list, &tmp); | |
| spin_unlock_irqrestore(&ip->i_ioend_lock, flags); | |
| iomap_sort_ioends(&tmp); | |
| /* | |
| * We can allocate memory here while doing writeback on behalf of | |
| * memory reclaim. To avoid memory allocation deadlocks set the | |
| * task-wide nofs context for the following operations. | |
| */ | |
| nofs_flag = memalloc_nofs_save(); | |
| while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend, | |
| io_list))) { | |
| list_del_init(&ioend->io_list); | |
| iomap_ioend_try_merge(ioend, &tmp); | |
| if (xfs_is_zoned_inode(ip)) | |
| xfs_end_ioend_write_zoned(ioend); | |
| else | |
| xfs_end_ioend_write(ioend); | |
| cond_resched(); | |
| } | |
| memalloc_nofs_restore(nofs_flag); | |
| } | |
| void | |
| xfs_end_bio( | |
| struct bio *bio) | |
| { | |
| struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); | |
| struct xfs_inode *ip = XFS_I(ioend->io_inode); | |
| struct xfs_mount *mp = ip->i_mount; | |
| unsigned long flags; | |
| /* | |
| * For Appends record the actually written block number and set the | |
| * boundary flag if needed. | |
| */ | |
| if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) { | |
| ioend->io_sector = bio->bi_iter.bi_sector; | |
| xfs_mark_rtg_boundary(ioend); | |
| } | |
| spin_lock_irqsave(&ip->i_ioend_lock, flags); | |
| if (list_empty(&ip->i_ioend_list)) | |
| WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue, | |
| &ip->i_ioend_work)); | |
| list_add_tail(&ioend->io_list, &ip->i_ioend_list); | |
| spin_unlock_irqrestore(&ip->i_ioend_lock, flags); | |
| } |