aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2009-11-25 19:24:13 -0500
committerJames Bottomley <James.Bottomley@suse.de>2009-12-10 09:54:13 -0500
commitc982c368bb90adbd312faa05d0cfd842e9ab45a7 (patch)
treee5620557e3972c130e4b4b652d036a378d0f977b /drivers
parent78b9fb6d38b1caf1c11cba5b10bb859e3cce071f (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.c23
-rw-r--r--drivers/scsi/st.h1
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 */
3774static void normalize_buffer(struct st_buffer * STbuffer) 3776static 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)
3791static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count) 3793static 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
3823static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count) 3825static 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;