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

[SCSI] st: fix mdata->page_order handling

dio transfer always resets mdata->page_order to zero. It breaks
high-order pages previously allocated for non-dio transfer.

This patches adds reserved_page_order to st_buffer structure to save
page order for non-dio transfer.

http://bugzilla.kernel.org/show_bug.cgi?id=14563

When enlarge_buffer() allocates 524288 from 0, st uses six-order page
allocation. So mdata->page_order is 6 and frp_seg is 2.

After that, if st uses dio, sgl_map_user_pages() sets
mdata->page_order to 0 for st_do_scsi(). After that, when we call
normalize_buffer(), it frees only free frp_seg * PAGE_SIZE (2 * 4096)
though we should free frp_seg * PAGE_SIZE << 6 (2 * 4096 << 6). So we
see buffer_size is set to 516096 (524288 - 8192).

Reported-by: Joachim Breuer <linux-kernel@jmbreuer.net>
Tested-by: Joachim Breuer <linux-kernel@jmbreuer.net>
Acked-by: Kai Makisara <kai.makisara@kolumbus.fi>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Cc: stable@kernel.org
Signed-off-by: James Bottomley <James.Bottomley@suse.de>

authored by

FUJITA Tomonori and committed by
James Bottomley
c982c368 78b9fb6d

+13 -11
+12 -11
drivers/scsi/st.c
··· 552 552 SRpnt->waiting = waiting; 553 553 554 554 if (STp->buffer->do_dio) { 555 + mdata->page_order = 0; 555 556 mdata->nr_entries = STp->buffer->sg_segs; 556 557 mdata->pages = STp->buffer->mapped_pages; 557 558 } else { 559 + mdata->page_order = STp->buffer->reserved_page_order; 558 560 mdata->nr_entries = 559 561 DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order); 560 - STp->buffer->map_data.pages = STp->buffer->reserved_pages; 561 - STp->buffer->map_data.offset = 0; 562 + mdata->pages = STp->buffer->reserved_pages; 563 + mdata->offset = 0; 562 564 } 563 565 564 566 memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); ··· 3721 3719 priority |= __GFP_ZERO; 3722 3720 3723 3721 if (STbuffer->frp_segs) { 3724 - order = STbuffer->map_data.page_order; 3722 + order = STbuffer->reserved_page_order; 3725 3723 b_size = PAGE_SIZE << order; 3726 3724 } else { 3727 3725 for (b_size = PAGE_SIZE, order = 0; ··· 3754 3752 segs++; 3755 3753 } 3756 3754 STbuffer->b_data = page_address(STbuffer->reserved_pages[0]); 3757 - STbuffer->map_data.page_order = order; 3755 + STbuffer->reserved_page_order = order; 3758 3756 3759 3757 return 1; 3760 3758 } ··· 3767 3765 3768 3766 for (i=0; i < st_bp->frp_segs; i++) 3769 3767 memset(page_address(st_bp->reserved_pages[i]), 0, 3770 - PAGE_SIZE << st_bp->map_data.page_order); 3768 + PAGE_SIZE << st_bp->reserved_page_order); 3771 3769 st_bp->cleared = 1; 3772 3770 } 3773 3771 ··· 3775 3773 /* Release the extra buffer */ 3776 3774 static void normalize_buffer(struct st_buffer * STbuffer) 3777 3775 { 3778 - int i, order = STbuffer->map_data.page_order; 3776 + int i, order = STbuffer->reserved_page_order; 3779 3777 3780 3778 for (i = 0; i < STbuffer->frp_segs; i++) { 3781 3779 __free_pages(STbuffer->reserved_pages[i], order); ··· 3783 3781 } 3784 3782 STbuffer->frp_segs = 0; 3785 3783 STbuffer->sg_segs = 0; 3786 - STbuffer->map_data.page_order = 0; 3784 + STbuffer->reserved_page_order = 0; 3787 3785 STbuffer->map_data.offset = 0; 3788 3786 } 3789 3787 ··· 3793 3791 static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count) 3794 3792 { 3795 3793 int i, cnt, res, offset; 3796 - int length = PAGE_SIZE << st_bp->map_data.page_order; 3794 + int length = PAGE_SIZE << st_bp->reserved_page_order; 3797 3795 3798 3796 for (i = 0, offset = st_bp->buffer_bytes; 3799 3797 i < st_bp->frp_segs && offset >= length; i++) ··· 3825 3823 static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count) 3826 3824 { 3827 3825 int i, cnt, res, offset; 3828 - int length = PAGE_SIZE << st_bp->map_data.page_order; 3826 + int length = PAGE_SIZE << st_bp->reserved_page_order; 3829 3827 3830 3828 for (i = 0, offset = st_bp->read_pointer; 3831 3829 i < st_bp->frp_segs && offset >= length; i++) ··· 3858 3856 { 3859 3857 int src_seg, dst_seg, src_offset = 0, dst_offset; 3860 3858 int count, total; 3861 - int length = PAGE_SIZE << st_bp->map_data.page_order; 3859 + int length = PAGE_SIZE << st_bp->reserved_page_order; 3862 3860 3863 3861 if (offset == 0) 3864 3862 return; ··· 4580 4578 } 4581 4579 4582 4580 mdata->offset = uaddr & ~PAGE_MASK; 4583 - mdata->page_order = 0; 4584 4581 STbp->mapped_pages = pages; 4585 4582 4586 4583 return nr_pages;
+1
drivers/scsi/st.h
··· 46 46 struct st_request *last_SRpnt; 47 47 struct st_cmdstatus cmdstat; 48 48 struct page **reserved_pages; 49 + int reserved_page_order; 49 50 struct page **mapped_pages; 50 51 struct rq_map_data map_data; 51 52 unsigned char *b_data;