scsi: core: Avoid that ATA error handling can trigger a kernel hang or oops
authorBart Van Assche <bart.vanassche@wdc.com>
Thu, 22 Feb 2018 19:30:20 +0000 (11:30 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 2 Mar 2018 01:16:52 +0000 (20:16 -0500)
Avoid that the recently introduced call_rcu() call in the SCSI core
triggers a double call_rcu() call.

Reported-by: Natanael Copa <ncopa@alpinelinux.org>
Reported-by: Damien Le Moal <damien.lemoal@wdc.com>
References: https://bugzilla.kernel.org/show_bug.cgi?id=198861
Fixes: 3bd6f43f5cb3 ("scsi: core: Ensure that the SCSI error handler gets woken up")
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Reviewed-by: Damien Le Moal <damien.lemoal@wdc.com>
Tested-by: Damien Le Moal <damien.lemoal@wdc.com>
Cc: Natanael Copa <ncopa@alpinelinux.org>
Cc: Damien Le Moal <damien.lemoal@wdc.com>
Cc: Alexandre Oliva <oliva@gnu.org>
Cc: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/hosts.c
drivers/scsi/scsi_error.c
drivers/scsi/scsi_lib.c
include/scsi/scsi_cmnd.h
include/scsi/scsi_host.h

index 57bf43e..dd94649 100644 (file)
@@ -328,8 +328,6 @@ static void scsi_host_dev_release(struct device *dev)
        if (shost->work_q)
                destroy_workqueue(shost->work_q);
 
-       destroy_rcu_head(&shost->rcu);
-
        if (shost->shost_state == SHOST_CREATED) {
                /*
                 * Free the shost_dev device name here if scsi_host_alloc()
@@ -404,7 +402,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
        INIT_LIST_HEAD(&shost->starved_list);
        init_waitqueue_head(&shost->host_wait);
        mutex_init(&shost->scan_mutex);
-       init_rcu_head(&shost->rcu);
 
        index = ida_simple_get(&host_index_ida, 0, 0, GFP_KERNEL);
        if (index < 0)
index d042915..ca53a5f 100644 (file)
@@ -223,7 +223,8 @@ static void scsi_eh_reset(struct scsi_cmnd *scmd)
 
 static void scsi_eh_inc_host_failed(struct rcu_head *head)
 {
-       struct Scsi_Host *shost = container_of(head, typeof(*shost), rcu);
+       struct scsi_cmnd *scmd = container_of(head, typeof(*scmd), rcu);
+       struct Scsi_Host *shost = scmd->device->host;
        unsigned long flags;
 
        spin_lock_irqsave(shost->host_lock, flags);
@@ -259,7 +260,7 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
         * Ensure that all tasks observe the host state change before the
         * host_failed change.
         */
-       call_rcu(&shost->rcu, scsi_eh_inc_host_failed);
+       call_rcu(&scmd->rcu, scsi_eh_inc_host_failed);
 }
 
 /**
index 5cbc69b..4af1682 100644 (file)
@@ -670,6 +670,7 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
        if (!blk_rq_is_scsi(req)) {
                WARN_ON_ONCE(!(cmd->flags & SCMD_INITIALIZED));
                cmd->flags &= ~SCMD_INITIALIZED;
+               destroy_rcu_head(&cmd->rcu);
        }
 
        if (req->mq_ctx) {
@@ -1150,6 +1151,7 @@ static void scsi_initialize_rq(struct request *rq)
        struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
 
        scsi_req_init(&cmd->req);
+       init_rcu_head(&cmd->rcu);
        cmd->jiffies_at_alloc = jiffies;
        cmd->retries = 0;
 }
index 949a016..0382cea 100644 (file)
@@ -69,6 +69,9 @@ struct scsi_cmnd {
        struct list_head list;  /* scsi_cmnd participates in queue lists */
        struct list_head eh_entry; /* entry for the host eh_cmd_q */
        struct delayed_work abort_work;
+
+       struct rcu_head rcu;
+
        int eh_eflags;          /* Used by error handlr */
 
        /*
index 1a1df0d..a8b7bf8 100644 (file)
@@ -571,8 +571,6 @@ struct Scsi_Host {
                struct blk_mq_tag_set   tag_set;
        };
 
-       struct rcu_head rcu;
-
        atomic_t host_busy;                /* commands actually active on low-level */
        atomic_t host_blocked;