diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index 77acc26a85d9..8bb8b4085c8b 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -671,9 +671,13 @@ static int ntfs_prepare_mft_record_io_units(struct ntfs_inode *ni, * * We only write the mft record if the ntfs inode @ni is dirty. * - * On success, clean the mft record and return 0. - * On error (specifically ENOMEM), we redirty the record so it can be retried. - * For other errors, we mark the volume with errors. + * On success, clean the mft record and return 0. On ENOMEM, redirty the + * record so it can be retried. Asynchronous callers return success after + * redirtying while synchronous callers receive the error. For other errors, + * mark the volume with errors. + * + * If @sync is false, PG_writeback keeps the folio stable and serializes later + * writers until the I/O completes. */ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int sync) { @@ -693,8 +697,6 @@ 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); @@ -804,7 +806,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn ntfs_error(vol->sb, "Not enough memory to write mft record. Redirtying so the write is retried later."); mark_mft_record_dirty(ni); - err = 0; + if (!sync) + err = 0; } else NVolSetErrors(vol); return err; @@ -2959,7 +2962,7 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) * record to be freed is guaranteed to do it already. */ NInoSetDirty(ni); - err = write_mft_record(ni, ni_mrec, 0); + err = write_mft_record(ni, ni_mrec, 1); if (err) goto sync_rollback; diff --git a/fs/ntfs/mft.h b/fs/ntfs/mft.h index 28a79bf4c1c6..d2a31205e08c 100644 --- a/fs/ntfs/mft.h +++ b/fs/ntfs/mft.h @@ -53,17 +53,19 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn * @sync: if true, wait for i/o completion * * This is just a wrapper for write_mft_record_nolock() (see mft.c), which - * locks the folio for the duration of the write. The folio lock prevents - * races between writing the mft record via the dirty inode code paths and via - * page cache writeback or between neighbouring mft records residing in the - * same folio. + * locks the folio while preparing the write. write_mft_record_nolock() waits + * for prior folio writeback before modifying the folio and keeps PG_writeback + * set until the submitted I/O completes. Together these serialize dirty + * inode writes, page cache writeback, and neighbouring mft record writes in + * the same folio. * * Locking the page also serializes us against ->read_folio() if the page is not * uptodate. * * On success, clean the mft record and return 0. On allocation failure, - * redirty the record for retry and return 0. On other errors, return -errno - * and mark the volume with errors. + * redirty the record for retry. Asynchronous callers return 0 after + * redirtying, while synchronous callers receive -ENOMEM. On other errors, + * return -errno and mark the volume with errors. */ static inline int write_mft_record(struct ntfs_inode *ni, struct mft_record *m, int sync) {