Re: [2.6.14-rc1] sym scsi boot hang

Alan Stern
2005-09-14T15:49:49Z

On Wed, 14 Sep 2005, Anton Blanchard wrote:

> Hi,
>
> > If that's the cause, it's probably a double down of the host scan
> > semaphore somewhere in the code. alt-sysrq-t should work in this case,
> > can you get a stack trace of the blocked process?
>
> It appears to be this patch:
>
> [SCSI] SCSI core: fix leakage of scsi_cmnd's
>
> From: Alan Stern <stern@rowland.harvard.edu>

> And in particular it looks like the scsi_unprep_request in
> scsi_queue_insert is causing it. The following patch fixes the boot
> problems on the vscsi machine:

In general the scsi_unprep_request routine is correct and needs to be
there. The one part that might be questionable is the assignment to
req->special. It may turn out that the real solution is to have
scsi_execute set req->special to NULL; I assumed it would be NULL already
but perhaps I was wrong.

(James, I see a possible problem with scsi_insert_special_req. It adds to
the queue a request with REQ_DONTPREP set. How can such a request, with
no associated scsi_cmnd, ever work? Also, won't scsi_end_request and
__scsi_release_request end up putting the same scsi_command twice?)

Here is a patch that addresses the first problem and fixes up a few other
loose ends. Please see if it helps.

Alan Stern

Index: usb-2.6/drivers/scsi/scsi_lib.c
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_lib.c
+++ usb-2.6/drivers/scsi/scsi_lib.c
@@ -116,7 +116,13 @@ static void scsi_unprep_request(struct r
struct scsi_cmnd *cmd = req->special;

req->flags &= ~REQ_DONTPREP;
- req->special = (req->flags & REQ_SPECIAL) ? cmd->sc_request : NULL;
+ req->special = NULL;
+ if (req->flags & REQ_SPECIAL) {
+ struct scsi_request *sreq = cmd->sc_request;
+
+ if (sreq->sr_magic == SCSI_REQ_MAGIC)
+ req->special = sreq;
+ }

scsi_release_buffers(cmd);
scsi_put_command(cmd);
@@ -343,6 +349,7 @@ int scsi_execute(struct scsi_device *sde
req->sense_len = 0;
req->timeout = timeout;
req->flags |= flags | REQ_BLOCK_PC | REQ_SPECIAL | REQ_QUIET;
+ req->special = NULL;

/*
* head injection *required* here otherwise quiesce won't work
@@ -1072,9 +1079,6 @@ static int scsi_init_io(struct scsi_cmnd
printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors,
req->current_nr_sectors);

- /* release the command and kill it */
- scsi_release_buffers(cmd);
- scsi_put_command(cmd);
return BLKPREP_KILL;
}

@@ -1176,13 +1180,13 @@ static int scsi_prep_fn(struct request_q
if (req->flags & REQ_SPECIAL && req->special) {
struct scsi_request *sreq = req->special;

- if (sreq->sr_magic == SCSI_REQ_MAGIC) {
- cmd = scsi_get_command(sreq->sr_device, GFP_ATOMIC);
- if (unlikely(!cmd))
- goto defer;
- scsi_init_cmd_from_req(cmd, sreq);
- } else
- cmd = req->special;
+ if (sreq->sr_magic != SCSI_REQ_MAGIC)
+ printk(KERN_ERR "invalid sr_magic in %s\n",
+ __FUNCTION__);
+ cmd = scsi_get_command(sreq->sr_device, GFP_ATOMIC);
+ if (unlikely(!cmd))
+ goto defer;
+ scsi_init_cmd_from_req(cmd, sreq);
} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {

if(unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) {
@@ -1194,17 +1198,14 @@ static int scsi_prep_fn(struct request_q
sdev->host->host_no, sdev->id, sdev->lun);
goto kill;
}
-

/*
* Now try and find a command block that we can use.
*/
- if (!req->special) {
- cmd = scsi_get_command(sdev, GFP_ATOMIC);
- if (unlikely(!cmd))
- goto defer;
- } else
- cmd = req->special;
+ cmd = scsi_get_command(sdev, GFP_ATOMIC);
+ if (unlikely(!cmd))
+ goto defer;
+ cmd->sc_request = NULL;

/* pull a tag out of the request if we have one */
cmd->tag = req->tag;
@@ -1250,7 +1251,7 @@ static int scsi_prep_fn(struct request_q
ret = scsi_init_io(cmd);
switch(ret) {
case BLKPREP_KILL:
- /* BLKPREP_KILL return also releases the command */
+ scsi_unprep_request(req);
goto kill;
case BLKPREP_DEFER:
goto defer;