Re: [Bug 14563] SCSI tape driver: Spurious EIO and kernel BUG

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 18 Nov 2009 23:55:10 +0200 (EET)
Kai Makisara <Kai.Makisara@xxxxxxxxxxx> wrote:

> On Tue, 17 Nov 2009, Kai Makisara wrote:
> 
> > On Tue, 17 Nov 2009, linux-kernel@xxxxxxxxxxxx wrote:
> > 
> > > FUJITA Tomonori wrote:
> > > >>> Performing tape backups using amanda 2.6.0-p2 [using 512kB block size] leads to
> > > >>> spurious 'EIO' errors when writing to the tape; most of the time a kernel BUG
> > > >>> is hit shortly thereafter.
> > > >>>
> ...
> > I don't have enough energy tonight to experiment (sleep deprived ;-) but I 
> > have looked at the output and may have found something. The buffer size 
> > 516096 B = 504 kB = 126 * 4096 looks suspicious, especially with 512 kB 
> > fixed blocks. In this case the buffer should be at least one block. This 
> > is given to enlarge_buffer() as argument.
> > 
> > Looking at enlarge_buffer(), it seems to me that if the allocation loop 
> > near the end fails with segs < max_segs before got > new_size, we have a 
> > bug: the allocation actually fails but it is returned as success. However, 
> > I don't have any explanation why this would happen.
> > 
> > I will continue tomorrow (after work).
> > 
> OK, I have continued but not found the bug. I have also read the other 
> messages from today. The problem seems to be related to direct i/o.
> 
> The logs from the problem case gives some information about what is 
> happening. This is from the log with variable block size:
> 
> st0: Number of r/w requests 86, dio used in 1, pages 128.                                                    
> st0: Block limits 1 - 16777215 bytes.                                                                        
> st0: Mode sense. Length 11, medium 0, WBS 10, BLL 8
> st0: Density 26, tape length: 0, drv buffer: 1
> st0: Block size: 0, buffer size: 516096 (1 blocks).
> 
> The first line is from closing the file. It shows that, in this case, 
> direct i/o was used in one write and the other ones have, for some reason, 
> used the internal buffer. The aic7xxx driver supports 128 scatter/gather 
> segments and so it is capable of using direct i/o up to 512 kB. It has 
> gone to using buffer for some other reason (alignment?).
> 
> The following lines are from the next open. The buffer is (should be) 
> deallocated at close() (normalize_buffer() called) but, in this case, 
> something is wrong.

As you suggested earlier, looks like normalize_buffer() doesn't free
all the buffers. I think that I found one bug that causes to this.

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).

Here's a fix for this.

Thanks,

diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 12d58a7..9c44828 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
 	SRpnt->waiting = waiting;
 
 	if (STp->buffer->do_dio) {
+		mdata->page_order = 0;
 		mdata->nr_entries = STp->buffer->sg_segs;
 		mdata->pages = STp->buffer->mapped_pages;
 	} else {
+		mdata->page_order = STp->buffer->reserved_page_order;
 		mdata->nr_entries =
 			DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order);
-		STp->buffer->map_data.pages = STp->buffer->reserved_pages;
-		STp->buffer->map_data.offset = 0;
+		mdata->pages = STp->buffer->reserved_pages;
+		mdata->offset = 0;
 	}
 
 	memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd));
@@ -3699,6 +3701,9 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm
 	int segs, nbr, max_segs, b_size, order, got;
 	gfp_t priority;
 
+	DEB(printk("%s: %d %u %d\n", __func__, new_size, STbuffer->buffer_size,
+		   STbuffer->reserved_page_order);)
+
 	if (new_size <= STbuffer->buffer_size)
 		return 1;
 
@@ -3718,7 +3723,7 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm
 		priority |= __GFP_ZERO;
 
 	if (STbuffer->frp_segs) {
-		order = STbuffer->map_data.page_order;
+		order = STbuffer->reserved_page_order;
 		b_size = PAGE_SIZE << order;
 	} else {
 		for (b_size = PAGE_SIZE, order = 0;
@@ -3751,7 +3756,9 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm
 		segs++;
 	}
 	STbuffer->b_data = page_address(STbuffer->reserved_pages[0]);
-	STbuffer->map_data.page_order = order;
+	STbuffer->reserved_page_order = order;
+
+	DEB(printk("%s: %d %u %d\n", __func__, new_size, STbuffer->buffer_size,	order);)
 
 	return 1;
 }
@@ -3764,7 +3771,7 @@ static void clear_buffer(struct st_buffer * st_bp)
 
 	for (i=0; i < st_bp->frp_segs; i++)
 		memset(page_address(st_bp->reserved_pages[i]), 0,
-		       PAGE_SIZE << st_bp->map_data.page_order);
+		       PAGE_SIZE << st_bp->reserved_page_order);
 	st_bp->cleared = 1;
 }
 
@@ -3772,7 +3779,7 @@ static void clear_buffer(struct st_buffer * st_bp)
 /* Release the extra buffer */
 static void normalize_buffer(struct st_buffer * STbuffer)
 {
-	int i, order = STbuffer->map_data.page_order;
+	int i, order = STbuffer->reserved_page_order;
 
 	for (i = 0; i < STbuffer->frp_segs; i++) {
 		__free_pages(STbuffer->reserved_pages[i], order);
@@ -3780,8 +3787,11 @@ static void normalize_buffer(struct st_buffer * STbuffer)
 	}
 	STbuffer->frp_segs = 0;
 	STbuffer->sg_segs = 0;
-	STbuffer->map_data.page_order = 0;
+	STbuffer->reserved_page_order = 0;
 	STbuffer->map_data.offset = 0;
+
+	if (STbuffer->buffer_size)
+		printk(KERN_ERR "%s: %u\n", __func__, STbuffer->buffer_size);
 }
 
 
@@ -3790,7 +3800,7 @@ static void normalize_buffer(struct st_buffer * STbuffer)
 static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count)
 {
 	int i, cnt, res, offset;
-	int length = PAGE_SIZE << st_bp->map_data.page_order;
+	int length = PAGE_SIZE << st_bp->reserved_page_order;
 
 	for (i = 0, offset = st_bp->buffer_bytes;
 	     i < st_bp->frp_segs && offset >= length; i++)
@@ -3822,7 +3832,7 @@ static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, in
 static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count)
 {
 	int i, cnt, res, offset;
-	int length = PAGE_SIZE << st_bp->map_data.page_order;
+	int length = PAGE_SIZE << st_bp->reserved_page_order;
 
 	for (i = 0, offset = st_bp->read_pointer;
 	     i < st_bp->frp_segs && offset >= length; i++)
@@ -3855,7 +3865,7 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset)
 {
 	int src_seg, dst_seg, src_offset = 0, dst_offset;
 	int count, total;
-	int length = PAGE_SIZE << st_bp->map_data.page_order;
+	int length = PAGE_SIZE << st_bp->reserved_page_order;
 
 	if (offset == 0)
 		return;
@@ -4577,7 +4587,6 @@ static int sgl_map_user_pages(struct st_buffer *STbp,
         }
 
 	mdata->offset = uaddr & ~PAGE_MASK;
-	mdata->page_order = 0;
 	STbp->mapped_pages = pages;
 
 	return nr_pages;
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index 544dc6b..f91a67c 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -46,6 +46,7 @@ struct st_buffer {
 	struct st_request *last_SRpnt;
 	struct st_cmdstatus cmdstat;
 	struct page **reserved_pages;
+	int reserved_page_order;
 	struct page **mapped_pages;
 	struct rq_map_data map_data;
 	unsigned char *b_data;
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux