diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2009-11-25 19:24:13 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-10 09:54:13 -0500 |
commit | c982c368bb90adbd312faa05d0cfd842e9ab45a7 (patch) | |
tree | e5620557e3972c130e4b4b652d036a378d0f977b /drivers | |
parent | 78b9fb6d38b1caf1c11cba5b10bb859e3cce071f (diff) |
[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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/st.c | 23 | ||||
-rw-r--r-- | drivers/scsi/st.h | 1 |
2 files changed, 13 insertions, 11 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index ad59abb47722..d04ea9a6f673 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c | |||
@@ -552,13 +552,15 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd | |||
552 | SRpnt->waiting = waiting; | 552 | SRpnt->waiting = waiting; |
553 | 553 | ||
554 | if (STp->buffer->do_dio) { | 554 | if (STp->buffer->do_dio) { |
555 | mdata->page_order = 0; | ||
555 | mdata->nr_entries = STp->buffer->sg_segs; | 556 | mdata->nr_entries = STp->buffer->sg_segs; |
556 | mdata->pages = STp->buffer->mapped_pages; | 557 | mdata->pages = STp->buffer->mapped_pages; |
557 | } else { | 558 | } else { |
559 | mdata->page_order = STp->buffer->reserved_page_order; | ||
558 | mdata->nr_entries = | 560 | mdata->nr_entries = |
559 | DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order); | 561 | DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order); |
560 | STp->buffer->map_data.pages = STp->buffer->reserved_pages; | 562 | mdata->pages = STp->buffer->reserved_pages; |
561 | STp->buffer->map_data.offset = 0; | 563 | mdata->offset = 0; |
562 | } | 564 | } |
563 | 565 | ||
564 | memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); | 566 | memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); |
@@ -3719,7 +3721,7 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm | |||
3719 | priority |= __GFP_ZERO; | 3721 | priority |= __GFP_ZERO; |
3720 | 3722 | ||
3721 | if (STbuffer->frp_segs) { | 3723 | if (STbuffer->frp_segs) { |
3722 | order = STbuffer->map_data.page_order; | 3724 | order = STbuffer->reserved_page_order; |
3723 | b_size = PAGE_SIZE << order; | 3725 | b_size = PAGE_SIZE << order; |
3724 | } else { | 3726 | } else { |
3725 | for (b_size = PAGE_SIZE, order = 0; | 3727 | for (b_size = PAGE_SIZE, order = 0; |
@@ -3752,7 +3754,7 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm | |||
3752 | segs++; | 3754 | segs++; |
3753 | } | 3755 | } |
3754 | STbuffer->b_data = page_address(STbuffer->reserved_pages[0]); | 3756 | STbuffer->b_data = page_address(STbuffer->reserved_pages[0]); |
3755 | STbuffer->map_data.page_order = order; | 3757 | STbuffer->reserved_page_order = order; |
3756 | 3758 | ||
3757 | return 1; | 3759 | return 1; |
3758 | } | 3760 | } |
@@ -3765,7 +3767,7 @@ static void clear_buffer(struct st_buffer * st_bp) | |||
3765 | 3767 | ||
3766 | for (i=0; i < st_bp->frp_segs; i++) | 3768 | for (i=0; i < st_bp->frp_segs; i++) |
3767 | memset(page_address(st_bp->reserved_pages[i]), 0, | 3769 | memset(page_address(st_bp->reserved_pages[i]), 0, |
3768 | PAGE_SIZE << st_bp->map_data.page_order); | 3770 | PAGE_SIZE << st_bp->reserved_page_order); |
3769 | st_bp->cleared = 1; | 3771 | st_bp->cleared = 1; |
3770 | } | 3772 | } |
3771 | 3773 | ||
@@ -3773,7 +3775,7 @@ static void clear_buffer(struct st_buffer * st_bp) | |||
3773 | /* Release the extra buffer */ | 3775 | /* Release the extra buffer */ |
3774 | static void normalize_buffer(struct st_buffer * STbuffer) | 3776 | static void normalize_buffer(struct st_buffer * STbuffer) |
3775 | { | 3777 | { |
3776 | int i, order = STbuffer->map_data.page_order; | 3778 | int i, order = STbuffer->reserved_page_order; |
3777 | 3779 | ||
3778 | for (i = 0; i < STbuffer->frp_segs; i++) { | 3780 | for (i = 0; i < STbuffer->frp_segs; i++) { |
3779 | __free_pages(STbuffer->reserved_pages[i], order); | 3781 | __free_pages(STbuffer->reserved_pages[i], order); |
@@ -3781,7 +3783,7 @@ static void normalize_buffer(struct st_buffer * STbuffer) | |||
3781 | } | 3783 | } |
3782 | STbuffer->frp_segs = 0; | 3784 | STbuffer->frp_segs = 0; |
3783 | STbuffer->sg_segs = 0; | 3785 | STbuffer->sg_segs = 0; |
3784 | STbuffer->map_data.page_order = 0; | 3786 | STbuffer->reserved_page_order = 0; |
3785 | STbuffer->map_data.offset = 0; | 3787 | STbuffer->map_data.offset = 0; |
3786 | } | 3788 | } |
3787 | 3789 | ||
@@ -3791,7 +3793,7 @@ static void normalize_buffer(struct st_buffer * STbuffer) | |||
3791 | static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count) | 3793 | static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count) |
3792 | { | 3794 | { |
3793 | int i, cnt, res, offset; | 3795 | int i, cnt, res, offset; |
3794 | int length = PAGE_SIZE << st_bp->map_data.page_order; | 3796 | int length = PAGE_SIZE << st_bp->reserved_page_order; |
3795 | 3797 | ||
3796 | for (i = 0, offset = st_bp->buffer_bytes; | 3798 | for (i = 0, offset = st_bp->buffer_bytes; |
3797 | i < st_bp->frp_segs && offset >= length; i++) | 3799 | i < st_bp->frp_segs && offset >= length; i++) |
@@ -3823,7 +3825,7 @@ static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, in | |||
3823 | static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count) | 3825 | static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count) |
3824 | { | 3826 | { |
3825 | int i, cnt, res, offset; | 3827 | int i, cnt, res, offset; |
3826 | int length = PAGE_SIZE << st_bp->map_data.page_order; | 3828 | int length = PAGE_SIZE << st_bp->reserved_page_order; |
3827 | 3829 | ||
3828 | for (i = 0, offset = st_bp->read_pointer; | 3830 | for (i = 0, offset = st_bp->read_pointer; |
3829 | i < st_bp->frp_segs && offset >= length; i++) | 3831 | i < st_bp->frp_segs && offset >= length; i++) |
@@ -3856,7 +3858,7 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset) | |||
3856 | { | 3858 | { |
3857 | int src_seg, dst_seg, src_offset = 0, dst_offset; | 3859 | int src_seg, dst_seg, src_offset = 0, dst_offset; |
3858 | int count, total; | 3860 | int count, total; |
3859 | int length = PAGE_SIZE << st_bp->map_data.page_order; | 3861 | int length = PAGE_SIZE << st_bp->reserved_page_order; |
3860 | 3862 | ||
3861 | if (offset == 0) | 3863 | if (offset == 0) |
3862 | return; | 3864 | return; |
@@ -4578,7 +4580,6 @@ static int sgl_map_user_pages(struct st_buffer *STbp, | |||
4578 | } | 4580 | } |
4579 | 4581 | ||
4580 | mdata->offset = uaddr & ~PAGE_MASK; | 4582 | mdata->offset = uaddr & ~PAGE_MASK; |
4581 | mdata->page_order = 0; | ||
4582 | STbp->mapped_pages = pages; | 4583 | STbp->mapped_pages = pages; |
4583 | 4584 | ||
4584 | return nr_pages; | 4585 | return nr_pages; |
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 544dc6b1f548..f91a67c6d968 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h | |||
@@ -46,6 +46,7 @@ struct st_buffer { | |||
46 | struct st_request *last_SRpnt; | 46 | struct st_request *last_SRpnt; |
47 | struct st_cmdstatus cmdstat; | 47 | struct st_cmdstatus cmdstat; |
48 | struct page **reserved_pages; | 48 | struct page **reserved_pages; |
49 | int reserved_page_order; | ||
49 | struct page **mapped_pages; | 50 | struct page **mapped_pages; |
50 | struct rq_map_data map_data; | 51 | struct rq_map_data map_data; |
51 | unsigned char *b_data; | 52 | unsigned char *b_data; |