Skip to content

Commit 36ee131

Browse files
committed
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>
1 parent 6e0152c commit 36ee131

1 file changed

Lines changed: 19 additions & 7 deletions

File tree

fs/ntfs/iomap.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, lo
8989
u32 attr_len;
9090
int err = 0;
9191
char *kattr;
92+
struct page *ipage;
9293

9394
if (NInoAttr(ni))
9495
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
129130

130131
kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset);
131132

132-
iomap->inline_data = kmemdup(kattr, attr_len, GFP_KERNEL);
133-
if (!iomap->inline_data) {
133+
ipage = alloc_page(GFP_NOFS | __GFP_ZERO);
134+
if (!ipage) {
134135
err = -ENOMEM;
135136
goto out;
136137
}
137138

139+
memcpy(page_address(ipage), kattr, attr_len);
138140
iomap->type = IOMAP_INLINE;
141+
iomap->inline_data = page_address(ipage);
139142
iomap->offset = 0;
140143
iomap->length = attr_len;
144+
iomap->private = ipage;
141145

142146
out:
143147
if (ctx)
@@ -285,8 +289,11 @@ static int ntfs_read_iomap_begin(struct inode *inode, loff_t offset, loff_t leng
285289
static int ntfs_read_iomap_end(struct inode *inode, loff_t pos, loff_t length,
286290
ssize_t written, unsigned int flags, struct iomap *iomap)
287291
{
288-
if (iomap->type == IOMAP_INLINE)
289-
kfree(iomap->inline_data);
292+
if (iomap->type == IOMAP_INLINE) {
293+
struct page *ipage = iomap->private;
294+
295+
put_page(ipage);
296+
}
290297

291298
return written;
292299
}
@@ -652,6 +659,7 @@ static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset,
652659
u32 attr_len;
653660
int err = 0;
654661
char *kattr;
662+
struct page *ipage;
655663

656664
ctx = ntfs_attr_get_search_ctx(ni, NULL);
657665
if (!ctx) {
@@ -672,16 +680,19 @@ static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset,
672680
attr_len = le32_to_cpu(a->data.resident.value_length);
673681
kattr = (u8 *)a + le16_to_cpu(a->data.resident.value_offset);
674682

675-
iomap->inline_data = kmemdup(kattr, attr_len, GFP_KERNEL);
676-
if (!iomap->inline_data) {
683+
ipage = alloc_page(GFP_NOFS | __GFP_ZERO);
684+
if (!ipage) {
677685
err = -ENOMEM;
678686
goto out;
679687
}
680688

689+
memcpy(page_address(ipage), kattr, attr_len);
681690
iomap->type = IOMAP_INLINE;
691+
iomap->inline_data = page_address(ipage);
682692
iomap->offset = 0;
683693
/* iomap requires there is only one INLINE_DATA extent */
684694
iomap->length = attr_len;
695+
iomap->private = ipage;
685696

686697
out:
687698
if (ctx)
@@ -771,6 +782,7 @@ static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos,
771782
u32 attr_len;
772783
int err;
773784
char *kattr;
785+
struct page *ipage = iomap->private;
774786

775787
mutex_lock(&ni->mrec_lock);
776788
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,
799811
mark_mft_record_dirty(ctx->ntfs_ino);
800812
err_out:
801813
ntfs_attr_put_search_ctx(ctx);
802-
kfree(iomap->inline_data);
814+
put_page(ipage);
803815
mutex_unlock(&ni->mrec_lock);
804816
return written;
805817

0 commit comments

Comments
 (0)