Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+19 -7
+19 -7
fs/ntfs/iomap.c
··· 89 89 u32 attr_len; 90 90 int err = 0; 91 91 char *kattr; 92 + struct page *ipage; 92 93 93 94 if (NInoAttr(ni)) 94 95 base_ni = ni->ext.base_ntfs_ino; ··· 130 129 131 130 kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); 132 131 133 - iomap->inline_data = kmemdup(kattr, attr_len, GFP_KERNEL); 134 - if (!iomap->inline_data) { 132 + ipage = alloc_page(GFP_NOFS | __GFP_ZERO); 133 + if (!ipage) { 135 134 err = -ENOMEM; 136 135 goto out; 137 136 } 138 137 138 + memcpy(page_address(ipage), kattr, attr_len); 139 139 iomap->type = IOMAP_INLINE; 140 + iomap->inline_data = page_address(ipage); 140 141 iomap->offset = 0; 141 142 iomap->length = attr_len; 143 + iomap->private = ipage; 142 144 143 145 out: 144 146 if (ctx) ··· 289 285 static int ntfs_read_iomap_end(struct inode *inode, loff_t pos, loff_t length, 290 286 ssize_t written, unsigned int flags, struct iomap *iomap) 291 287 { 292 - if (iomap->type == IOMAP_INLINE) 293 - kfree(iomap->inline_data); 288 + if (iomap->type == IOMAP_INLINE) { 289 + struct page *ipage = iomap->private; 290 + 291 + put_page(ipage); 292 + } 294 293 295 294 return written; 296 295 } ··· 659 652 u32 attr_len; 660 653 int err = 0; 661 654 char *kattr; 655 + struct page *ipage; 662 656 663 657 ctx = ntfs_attr_get_search_ctx(ni, NULL); 664 658 if (!ctx) { ··· 680 672 attr_len = le32_to_cpu(a->data.resident.value_length); 681 673 kattr = (u8 *)a + le16_to_cpu(a->data.resident.value_offset); 682 674 683 - iomap->inline_data = kmemdup(kattr, attr_len, GFP_KERNEL); 684 - if (!iomap->inline_data) { 675 + ipage = alloc_page(GFP_NOFS | __GFP_ZERO); 676 + if (!ipage) { 685 677 err = -ENOMEM; 686 678 goto out; 687 679 } 688 680 681 + memcpy(page_address(ipage), kattr, attr_len); 689 682 iomap->type = IOMAP_INLINE; 683 + iomap->inline_data = page_address(ipage); 690 684 iomap->offset = 0; 691 685 /* iomap requires there is only one INLINE_DATA extent */ 692 686 iomap->length = attr_len; 687 + iomap->private = ipage; 693 688 694 689 out: 695 690 if (ctx) ··· 782 771 u32 attr_len; 783 772 int err; 784 773 char *kattr; 774 + struct page *ipage = iomap->private; 785 775 786 776 mutex_lock(&ni->mrec_lock); 787 777 ctx = ntfs_attr_get_search_ctx(ni, NULL); ··· 811 799 mark_mft_record_dirty(ctx->ntfs_ino); 812 800 err_out: 813 801 ntfs_attr_put_search_ctx(ctx); 814 - kfree(iomap->inline_data); 802 + put_page(ipage); 815 803 mutex_unlock(&ni->mrec_lock); 816 804 return written; 817 805