Re: SCSI bug

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

 



On 2016-02-21, at 10:24 PM, John David Anglin wrote:

> On 2016-02-21, at 7:53 PM, John David Anglin wrote:
> 
>> Backtrace:                                                                      
>> [<000000004046f4b0>] scsi_init_sgtable+0x70/0xb8                               
>> [<000000004046f564>] scsi_init_io+0x6c/0x220                                   
>> [<000000000811c5c0>] sd_setup_read_write_cmnd+0x58/0x968 [sd_mod]              
>> [<000000000811cf14>] sd_init_command+0x44/0x130 [sd_mod]                       
>> [<000000004046f81c>] scsi_setup_cmnd+0x104/0x1b0                               
>> [<000000004046fab8>] scsi_prep_fn+0x100/0x1a0                                  
>> [<000000004035b9b0>] blk_peek_request+0x1b8/0x298                              
>> [<0000000040471028>] scsi_request_fn+0xf8/0xa90                                
>> [<0000000040357244>] __blk_run_queue+0x4c/0x70                                 
>> [<00000000403802c4>] cfq_insert_request+0x2dc/0x580                            
>> [<0000000040356404>] __elv_add_request+0x1b4/0x300                             
>> 
>> We have in blk-merge.c:
>> 
>>               else {
>>                       /*
>>                        * If the driver previously mapped a shorter
>>                        * list, we could see a termination bit
>>                        * prematurely unless it fully inits the sg
>>                        * table on each mapping. We KNOW that there
>>                        * must be more entries here or the driver
>>                        * would be buggy, so force clear the
>>                        * termination bit to avoid doing a full
>>                        * sg_init_table() in drivers for each command.
>>                        */
>>                       if (sg_is_last (*sg))
>>                         printk ("__blk_segment_map_sg: clearing termination bi
>> t\n");
>>                       sg_unmark_end(*sg);
>>                       *sg = sg_next(*sg);
>>                       BUG_ON (!*sg);
>>               }
>> 
>> The comment suggests there must be more entries...
> 
> I'm thinking with the split the scsi driver needs to provide one or two extra entires in the sg list.


With the attached patch, I'm able to boot 4.2.0-rc2+ on linux-block at commit
54efd50bfd873e2dbf784e0b21a8027ba4299a3e.

I didn't try to optimize the number of extra entries but I know one is not enough.

I guess the puzzle is why the number of entries isn't calculated correctly in the first place.
Further, why does blk-merge believe that it's okay to go beyond the terminator?  Clearly,
the magic number isn't always set, etc.

I added the WARN_ON so I'd know when we run off the end of the the list.

Dave
--
John David Anglin	dave.anglin@xxxxxxxx


diff --git a/block/blk-merge.c b/block/blk-merge.c
index d9c3a75..8e2566b 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -327,6 +327,7 @@ new_segment:
 			 * termination bit to avoid doing a full
 			 * sg_init_table() in drivers for each command.
 			 */
+			WARN_ON(sg_is_last (*sg));
 			sg_unmark_end(*sg);
 			*sg = sg_next(*sg);
 		}
@@ -392,6 +393,9 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq,
 	if (rq->bio)
 		nsegs = __blk_bios_map_sg(q, rq->bio, sglist, &sg);
 
+	if (!sg)
+		return nsegs;
+
 	if (unlikely(rq->cmd_flags & REQ_COPY_USER) &&
 	    (blk_rq_bytes(rq) & q->dma_pad_mask)) {
 		unsigned int pad_len =
@@ -415,8 +419,7 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq,
 		rq->extra_len += q->dma_drain_size;
 	}
 
-	if (sg)
-		sg_mark_end(sg);
+	sg_mark_end(sg);
 
 	return nsegs;
 }
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index b1a2631..b421f03 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -595,6 +595,11 @@ static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, bool mq)
 
 	BUG_ON(!nents);
 
+	/* Provide extra entries in case of split.  */
+	nents += 8;
+	if (nents > SCSI_MAX_SG_SEGMENTS)
+		nents = SCSI_MAX_SG_SEGMENTS;
+
 	if (mq) {
 		if (nents <= SCSI_MAX_SG_SEGMENTS) {
 			sdb->table.nents = nents;

[Index of Archives]     [Linux SoC]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux