7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Hyunchul Lee [ Upstream commit 4437d9d787185520c4798a942de9b5ef986d2141 ] Use the configured MFT I/O unit for direct writes, mirror updates, and generic folio writeback. On native 4Kn devices, multiple MFT records can share a device block, so submit complete containing units while preserving unselected neighboring records. Track selected records separately from records owned by direct inode writeback, and retain the existing runlist and cluster-boundary checks. Force direct MFT writes to complete synchronously when the I/O unit is larger than an individual record. the following patch restores caller-controlled completion. Fixes: 115380f9a2f9 ("ntfs: update mft operations") Cc: stable@vger.kernel.org Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/mft.c | 61 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index bbbe0d4426ecf..77acc26a85d90 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -597,13 +597,18 @@ static int ntfs_sync_mft_mirror_unit(struct ntfs_volume *vol, static int ntfs_sync_mft_mirror_record(struct ntfs_volume *vol, struct folio *source, const u64 mft_no) { + u64 mirror_file_ofs = (u64)mft_no * vol->mft_record_size; struct ntfs_mft_io_unit unit = { .folio_ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no), .len = vol->mft_record_size, }; - return ntfs_sync_mft_mirror_unit(vol, source, - (u64)mft_no * vol->mft_record_size, &unit); + mirror_file_ofs = + round_down(mirror_file_ofs, (u64)vol->mft_io_unit_size); + unit.folio_ofs = round_down(unit.folio_ofs, vol->mft_io_unit_size); + unit.len = vol->mft_io_unit_size; + + return ntfs_sync_mft_mirror_unit(vol, source, mirror_file_ofs, &unit); } static int ntfs_prepare_mft_record_io_units(struct ntfs_inode *ni, @@ -623,6 +628,18 @@ static int ntfs_prepare_mft_record_io_units(struct ntfs_inode *ni, return -EIO; cluster_ofs = ntfs_bytes_to_cluster_off(vol, record_byte); + if (vol->mft_io_unit_size > vol->mft_record_size) { + cluster_ofs = round_down(cluster_ofs, vol->mft_io_unit_size); + disk_byte = NTFS_CLU_TO_B(vol, ni->mft_lcn[0]) + cluster_ofs; + units[0] = (struct ntfs_mft_io_unit){ + .sector = ntfs_bytes_to_bio_sector(disk_byte), + .folio_ofs = round_down(ni->folio_ofs, + vol->mft_io_unit_size), + .len = vol->mft_io_unit_size, + }; + return 1; + } + disk_byte = NTFS_CLU_TO_B(vol, ni->mft_lcn[0]) + cluster_ofs; units[0] = (struct ntfs_mft_io_unit){ .sector = ntfs_bytes_to_bio_sector(disk_byte), @@ -676,6 +693,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn WARN_ON(NInoAttr(ni)); WARN_ON(!folio_test_locked(folio)); + if (vol->mft_io_unit_size > vol->mft_record_size) + sync = 1; if (folio_test_writeback(folio)) folio_wait_writeback(folio); @@ -3063,7 +3082,7 @@ static int ntfs_map_mft_io_for_folio(struct ntfs_inode *ni, u64 folio_byte, static int ntfs_prepare_mft_folio_units(struct ntfs_inode *ni, u64 folio_byte, u64 file_limit, - const unsigned long *record_writable, + const unsigned long *record_selected, struct ntfs_mft_io_unit *units, unsigned int *nr_units, unsigned int max_units, bool *defer) @@ -3075,28 +3094,37 @@ static int ntfs_prepare_mft_folio_units(struct ntfs_inode *ni, u64 folio_byte, while (unit_byte < folio_end && unit_byte < file_limit) { struct ntfs_mft_io_unit unit; u64 record_byte; - u64 cluster_end = ntfs_cluster_to_bytes( - vol, ntfs_bytes_to_cluster(vol, unit_byte) + 1); - u64 record_end = - round_down(unit_byte, (u64)vol->mft_record_size) + - vol->mft_record_size; - u64 unit_end = min3(record_end, cluster_end, folio_end); - bool writable = false; + u64 unit_end; + u64 io_unit_end; + u64 cluster_end; + bool selected = false; int err; + io_unit_end = + round_down(unit_byte, (u64)vol->mft_io_unit_size) + + vol->mft_io_unit_size; + cluster_end = ntfs_cluster_to_bytes( + vol, ntfs_bytes_to_cluster(vol, unit_byte) + 1); + unit_end = min3(io_unit_end, cluster_end, folio_end); + record_byte = round_down(unit_byte, (u64)vol->mft_record_size); while (record_byte < min(unit_end, file_limit)) { unsigned int record; record = div_u64(record_byte - folio_byte, vol->mft_record_size); - if (test_bit(record, record_writable)) { - writable = true; + if (test_bit(record, record_selected)) { + selected = true; break; } record_byte += vol->mft_record_size; } - if (!writable) + /* + * Direct inode writeback owns unselected records. Their folio + * images are stable while this folio is locked, so a containing + * unit can preserve them when writing a selected record. + */ + if (!selected) goto next; unit = (struct ntfs_mft_io_unit){ @@ -3153,7 +3181,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w struct bio *parent = NULL, *child = NULL; struct ntfs_mft_write_ctx *ctx = NULL; u8 *kaddr = NULL; - DECLARE_BITMAP(record_writable, PAGE_SIZE / NTFS_BLOCK_SIZE) = {}; + DECLARE_BITMAP(record_selected, PAGE_SIZE / NTFS_BLOCK_SIZE) = {}; u64 folio_byte, file_limit, folio_end, mirror_size; unsigned int nr_records, nr_units = 0, max_units; unsigned int nr_locked_nis = 0, nr_ref_inos = 0; @@ -3221,11 +3249,11 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w tni->ext.base_ntfs_ino == NTFS_I(vol->mft_ino)) continue; } - __set_bit(record, record_writable); + __set_bit(record, record_selected); } err = ntfs_prepare_mft_folio_units(ni, folio_byte, file_limit, - record_writable, units, &nr_units, + record_selected, units, &nr_units, max_units, &defer); if (err) goto out_noio; @@ -3309,6 +3337,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w submit_bio(parent); ntfs_release_mft_write_refs(locked_nis, nr_locked_nis, ref_inos, nr_ref_inos); + return 0; out_noio: -- 2.53.0