diff options
| author | Namjae Jeon <linkinjeon@kernel.org> | 2026-04-22 11:56:12 +0900 |
|---|---|---|
| committer | Namjae Jeon <linkinjeon@kernel.org> | 2026-04-22 19:05:07 +0900 |
| commit | 36ee1313199b7f16bf963c6ac0241861585125d9 (patch) | |
| tree | 1b22487eaf912311bf05c220d355264a72c82010 | |
| parent | 6e0152c75d70725add4cef3b1cb10abc6efa6ad9 (diff) | |
ntfs: use page allocation for resident attribute inline data
The current kmemdup() based allocation for IOMAP_INLINE can result in
inline_data pointer having a non-zero page offset. This causes
iomap_inline_data_valid() to fail the check:
iomap->length <= PAGE_SIZE - offset_in_page(iomap->inline_data)
and triggers the kernel BUG at fs/iomap/buffered-io.c:1061.
This particularly affects workloads with frequent small file access
(e.g. Firefox Nightly profile on NTFS with bind mount) when using the
new ntfs. This fix this by allocating a full page with alloc_page() so that
page_address() always returns a page-aligned address.
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
| -rw-r--r-- | fs/ntfs/iomap.c | 26 |
1 files changed, 19 insertions, 7 deletions
diff --git a/fs/ntfs/iomap.c b/fs/ntfs/iomap.c index 3d1458dea90f..74a4d3e971f4 100644 --- a/fs/ntfs/iomap.c +++ b/fs/ntfs/iomap.c @@ -89,6 +89,7 @@ static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, lo u32 attr_len; int err = 0; char *kattr; + struct page *ipage; if (NInoAttr(ni)) base_ni = ni->ext.base_ntfs_ino; @@ -129,15 +130,18 @@ static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, lo kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); - iomap->inline_data = kmemdup(kattr, attr_len, GFP_KERNEL); - if (!iomap->inline_data) { + ipage = alloc_page(GFP_NOFS | __GFP_ZERO); + if (!ipage) { err = -ENOMEM; goto out; } + memcpy(page_address(ipage), kattr, attr_len); iomap->type = IOMAP_INLINE; + iomap->inline_data = page_address(ipage); iomap->offset = 0; iomap->length = attr_len; + iomap->private = ipage; out: if (ctx) @@ -285,8 +289,11 @@ static int ntfs_read_iomap_begin(struct inode *inode, loff_t offset, loff_t leng static int ntfs_read_iomap_end(struct inode *inode, loff_t pos, loff_t length, ssize_t written, unsigned int flags, struct iomap *iomap) { - if (iomap->type == IOMAP_INLINE) - kfree(iomap->inline_data); + if (iomap->type == IOMAP_INLINE) { + struct page *ipage = iomap->private; + + put_page(ipage); + } return written; } @@ -652,6 +659,7 @@ static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset, u32 attr_len; int err = 0; char *kattr; + struct page *ipage; ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { @@ -672,16 +680,19 @@ static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset, attr_len = le32_to_cpu(a->data.resident.value_length); kattr = (u8 *)a + le16_to_cpu(a->data.resident.value_offset); - iomap->inline_data = kmemdup(kattr, attr_len, GFP_KERNEL); - if (!iomap->inline_data) { + ipage = alloc_page(GFP_NOFS | __GFP_ZERO); + if (!ipage) { err = -ENOMEM; goto out; } + memcpy(page_address(ipage), kattr, attr_len); iomap->type = IOMAP_INLINE; + iomap->inline_data = page_address(ipage); iomap->offset = 0; /* iomap requires there is only one INLINE_DATA extent */ iomap->length = attr_len; + iomap->private = ipage; out: if (ctx) @@ -771,6 +782,7 @@ static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos, u32 attr_len; int err; char *kattr; + struct page *ipage = iomap->private; mutex_lock(&ni->mrec_lock); ctx = ntfs_attr_get_search_ctx(ni, NULL); @@ -799,7 +811,7 @@ static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos, mark_mft_record_dirty(ctx->ntfs_ino); err_out: ntfs_attr_put_search_ctx(ctx); - kfree(iomap->inline_data); + put_page(ipage); mutex_unlock(&ni->mrec_lock); return written; |
