mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
ntfs: avoid truncate and writeback deadlock
ntfs_non_resident_attr_shrink() drops page-cache folios after freeing
clusters and truncating the runlist. The VFS truncate path calls it with
mrec_lock held. A folio writeback can need the same lock to resolve its
iomap mapping, so truncate can wait for writeback while writeback waits
for mrec_lock.
A hung-task call stack:
truncate: mrec_lock -> truncate_inode_pages() -> folio lock/writeback
writeback: folio lock/writeback -> ntfs_write_iomap_begin() ->
mrec_lock
ntfs_setattr_size() already calls truncate_setsize() before
ntfs_truncate_vfs() acquires mrec_lock. Reuse that page-cache truncation
in the VFS path and skip the duplicate truncation under mrec_lock. Keep
the cleanup for internal attribute callers, which do not call
truncate_setsize(). This avoids synchronously flushing the whole
discarded tail while preserving page-cache correctness.
Fixes: 4e646ecd44 ("ntfs: drop stale page-cache when shrinking a non-resident attr")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
67f27176e2
commit
fca746064d
|
|
@ -4234,12 +4234,14 @@ static int ntfs_attr_make_resident(struct ntfs_inode *ni, struct ntfs_attr_searc
|
|||
* ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute
|
||||
* @ni: non-resident ntfs attribute to shrink
|
||||
* @newsize: new size (in bytes) to which to shrink the attribute
|
||||
* @pagecache_truncated: page cache was already truncated to @newsize
|
||||
*
|
||||
* Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
|
||||
*/
|
||||
static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni,
|
||||
const s64 newsize,
|
||||
struct ntfs_inode *locked_ni)
|
||||
struct ntfs_inode *locked_ni,
|
||||
bool pagecache_truncated)
|
||||
{
|
||||
struct ntfs_volume *vol;
|
||||
struct ntfs_attr_search_ctx *ctx;
|
||||
|
|
@ -4389,7 +4391,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni,
|
|||
* later writeback map a vcn past the new allocation, which fails with
|
||||
* -ENOENT and loses the write.
|
||||
*/
|
||||
truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
|
||||
if (!pagecache_truncated)
|
||||
truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
|
||||
|
||||
/* Update data size in the index. */
|
||||
if (ni->type == AT_DATA && ni->name == AT_UNNAMED)
|
||||
|
|
@ -4991,7 +4994,7 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
|||
up_write(&ni->runlist.lock);
|
||||
} else
|
||||
err = ntfs_non_resident_attr_shrink(
|
||||
ni, newsize, NULL);
|
||||
ni, newsize, NULL, true);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
|
|
@ -5104,7 +5107,7 @@ int ntfs_attr_truncate_i_locked(struct ntfs_inode *ni, const s64 newsize,
|
|||
ni, newsize, 0, holes, locked_ni);
|
||||
else
|
||||
err = ntfs_non_resident_attr_shrink(
|
||||
ni, newsize, locked_ni);
|
||||
ni, newsize, locked_ni, false);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, 0, holes);
|
||||
ntfs_debug("Return status %d\n", err);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user