[This message is completely separate from the concurrent discussion of
earlier patches, as542-as546; this patch fixes a different problem.]
James:
The SCSI core has a problem with leakage of scsi_cmnd structs. It occurs
when a request is requeued; the request keeps its associated scsi_cmnd so
that the core doesn't need to assign a new one. However, the routines
that read the device queue sometimes delete entries without bothering to
free the associated scsi_cmnd. Among other things, this bug manifests as
error-handler processes remaining alive when a hot-pluggable device is
unplugged in the middle of executing a command.
This patch (as559) fixes the problem by calling scsi_put_command and
scsi_release_buffers at the appropriate times. It also reorganizes a
couple of bits of code, adding a new subroutine to find the scsi_cmnd
associated with a requeued request.
This addresses Bugzilla entry #5195.
Alan Stern
Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
Index: 2613mm1/drivers/scsi/scsi_lib.c
===================================================================
--- 2613mm1.orig/drivers/scsi/scsi_lib.c
+++ 2613mm1/drivers/scsi/scsi_lib.c
@@ -1103,11 +1103,30 @@ static void scsi_generic_done(struct scs
scsi_io_completion(cmd, cmd->result == 0 ? cmd->bufflen : 0, 0);
}
+static struct scsi_cmnd *scsi_cmd_from_req(struct request *req)
+{
+ struct scsi_cmnd *cmd;
+
+ /*
+ * If this request was requeued, it may already have an associated
+ * scsi_cmnd. Find out if this is so and return the cmd.
+ */
+ cmd = req->special;
+ if ((req->flags & REQ_SPECIAL) && req->special) {
+ struct scsi_request *sreq = req->special;
+
+ if (sreq->sr_magic == SCSI_REQ_MAGIC)
+ cmd = NULL;
+ }
+ return cmd;
+}
+
static int scsi_prep_fn(struct request_queue *q, struct request *req)
{
struct scsi_device *sdev = q->queuedata;
- struct scsi_cmnd *cmd;
+ struct scsi_cmnd *cmd = scsi_cmd_from_req(req);
int specials_only = 0;
+ struct scsi_request *sreq = NULL;
/*
* Just check to see if the device is online. If it isn't, we
@@ -1117,6 +1136,8 @@ static int scsi_prep_fn(struct request_q
if (unlikely(!scsi_device_online(sdev))) {
printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline
device\n",
sdev->host->host_no, sdev->id, sdev->lun);
+ if (cmd)
+ scsi_put_command(cmd);
return BLKPREP_KILL;
}
if (unlikely(sdev->sdev_state != SDEV_RUNNING)) {
@@ -1127,6 +1148,8 @@ static int scsi_prep_fn(struct request_q
* at all allowed down */
printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to dead
device\n",
sdev->host->host_no, sdev->id, sdev->lun);
+ if (cmd)
+ scsi_put_command(cmd);
return BLKPREP_KILL;
}
/* OK, we only allow special commands (i.e. not
@@ -1142,53 +1165,54 @@ static int scsi_prep_fn(struct request_q
* the remainder of a partially fulfilled request that can
* come up when there is a medium error. We have to treat
* these two cases differently. We differentiate by looking
- * at request->cmd, as this tells us the real story.
+ * at req->special, as this tells us the real story.
*/
- if (req->flags & REQ_SPECIAL && req->special) {
- struct scsi_request *sreq = req->special;
+ if ((req->flags & REQ_SPECIAL) && req->special) {
+ sreq = req->special;
+ if (sreq->sr_magic != SCSI_REQ_MAGIC)
+ sreq = NULL;
- 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;
} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
-
- if(unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) {
- if(specials_only == SDEV_QUIESCE ||
+ if (unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) {
+ if (specials_only == SDEV_QUIESCE ||
specials_only == SDEV_BLOCK)
return BLKPREP_DEFER;
printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to
device being removed\n",
sdev->host->host_no, sdev->id, sdev->lun);
+ if (cmd)
+ scsi_put_command(cmd);
return BLKPREP_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;
-
- /* pull a tag out of the request if we have one */
- cmd->tag = req->tag;
+
} else {
blk_dump_rq_flags(req, "SCSI bad req");
+ if (cmd) /* Paranoia */
+ scsi_put_command(cmd);
return BLKPREP_KILL;
}
+
+ /*
+ * Now try and find a command block that we can use.
+ */
+ if (!cmd) {
+ cmd = scsi_get_command(sdev, GFP_ATOMIC);
+ if (unlikely(!cmd))
+ goto defer;
+
+ if (sreq)
+ scsi_init_cmd_from_req(cmd, sreq);
+ else {
+ /* pull a tag out of the request if we have one */
+ cmd->tag = req->tag;
+ }
- /* note the overloading of req->special. When the tag
- * is active it always means cmd. If the tag goes
- * back for re-queueing, it may be reset */
- req->special = cmd;
- cmd->request = req;
+ /* note the overloading of req->special. When the tag
+ * is active it always means cmd. If the tag goes
+ * back for re-queueing, it may be reset */
+ req->special = cmd;
+ cmd->request = req;
+ }
/*
* FIXME: drop the lock here because the functions below
@@ -1337,16 +1361,21 @@ static inline int scsi_host_queue_ready(
/*
* Kill requests for a dead device
*/
-static void scsi_kill_requests(request_queue_t *q)
+static void scsi_kill_request(struct request *req, struct request_queue *q)
{
- struct request *req;
+ struct scsi_cmnd *cmd = scsi_cmd_from_req(req);
+
+ blkdev_dequeue_request(req);
+ req->flags |= REQ_QUIET;
+ while (end_that_request_first(req, 0, req->nr_sectors))
+ ;
+ end_that_request_last(req);
- while ((req = elv_next_request(q)) != NULL) {
- blkdev_dequeue_request(req);
- req->flags |= REQ_QUIET;
- while (end_that_request_first(req, 0, req->nr_sectors))
- ;
- end_that_request_last(req);
+ if (cmd) {
+ spin_unlock(q->queue_lock);
+ scsi_release_buffers(cmd);
+ scsi_put_command(cmd);
+ spin_lock(q->queue_lock);
}
}
@@ -1370,7 +1399,8 @@ static void scsi_request_fn(struct reque
if (!sdev) {
printk("scsi: killing requests for dead queue\n");
- scsi_kill_requests(q);
+ while ((req = elv_next_request(q)) != NULL)
+ scsi_kill_request(req, q);
return;
}
@@ -1397,11 +1427,7 @@ static void scsi_request_fn(struct reque
if (unlikely(!scsi_device_online(sdev))) {
printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to
offline device\n",
sdev->host->host_no, sdev->id, sdev->lun);
- blkdev_dequeue_request(req);
- req->flags |= REQ_QUIET;
- while (end_that_request_first(req, 0, req->nr_sectors))
- ;
- end_that_request_last(req);
+ scsi_kill_request(req, q);
continue;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html