Commit 3be8828f authored by Bart Van Assche's avatar Bart Van Assche Committed by Martin K. Petersen

scsi: core: Avoid that ATA error handling can trigger a kernel hang or oops

Avoid that the recently introduced call_rcu() call in the SCSI core
triggers a double call_rcu() call.
Reported-by: default avatarNatanael Copa <ncopa@alpinelinux.org>
Reported-by: default avatarDamien Le Moal <damien.lemoal@wdc.com>
References: https://bugzilla.kernel.org/show_bug.cgi?id=198861
Fixes: 3bd6f43f ("scsi: core: Ensure that the SCSI error handler gets woken up")
Signed-off-by: default avatarBart Van Assche <bart.vanassche@wdc.com>
Reviewed-by: default avatarDamien Le Moal <damien.lemoal@wdc.com>
Tested-by: default avatarDamien 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: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent fa83e658
...@@ -328,8 +328,6 @@ static void scsi_host_dev_release(struct device *dev) ...@@ -328,8 +328,6 @@ static void scsi_host_dev_release(struct device *dev)
if (shost->work_q) if (shost->work_q)
destroy_workqueue(shost->work_q); destroy_workqueue(shost->work_q);
destroy_rcu_head(&shost->rcu);
if (shost->shost_state == SHOST_CREATED) { if (shost->shost_state == SHOST_CREATED) {
/* /*
* Free the shost_dev device name here if scsi_host_alloc() * 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) ...@@ -404,7 +402,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
INIT_LIST_HEAD(&shost->starved_list); INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait); init_waitqueue_head(&shost->host_wait);
mutex_init(&shost->scan_mutex); mutex_init(&shost->scan_mutex);
init_rcu_head(&shost->rcu);
index = ida_simple_get(&host_index_ida, 0, 0, GFP_KERNEL); index = ida_simple_get(&host_index_ida, 0, 0, GFP_KERNEL);
if (index < 0) if (index < 0)
......
...@@ -223,7 +223,8 @@ static void scsi_eh_reset(struct scsi_cmnd *scmd) ...@@ -223,7 +223,8 @@ static void scsi_eh_reset(struct scsi_cmnd *scmd)
static void scsi_eh_inc_host_failed(struct rcu_head *head) 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; unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
...@@ -259,7 +260,7 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scmd) ...@@ -259,7 +260,7 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
* Ensure that all tasks observe the host state change before the * Ensure that all tasks observe the host state change before the
* host_failed change. * host_failed change.
*/ */
call_rcu(&shost->rcu, scsi_eh_inc_host_failed); call_rcu(&scmd->rcu, scsi_eh_inc_host_failed);
} }
/** /**
......
...@@ -670,6 +670,7 @@ static bool scsi_end_request(struct request *req, blk_status_t error, ...@@ -670,6 +670,7 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
if (!blk_rq_is_scsi(req)) { if (!blk_rq_is_scsi(req)) {
WARN_ON_ONCE(!(cmd->flags & SCMD_INITIALIZED)); WARN_ON_ONCE(!(cmd->flags & SCMD_INITIALIZED));
cmd->flags &= ~SCMD_INITIALIZED; cmd->flags &= ~SCMD_INITIALIZED;
destroy_rcu_head(&cmd->rcu);
} }
if (req->mq_ctx) { if (req->mq_ctx) {
...@@ -1150,6 +1151,7 @@ static void scsi_initialize_rq(struct request *rq) ...@@ -1150,6 +1151,7 @@ static void scsi_initialize_rq(struct request *rq)
struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
scsi_req_init(&cmd->req); scsi_req_init(&cmd->req);
init_rcu_head(&cmd->rcu);
cmd->jiffies_at_alloc = jiffies; cmd->jiffies_at_alloc = jiffies;
cmd->retries = 0; cmd->retries = 0;
} }
......
...@@ -69,6 +69,9 @@ struct scsi_cmnd { ...@@ -69,6 +69,9 @@ struct scsi_cmnd {
struct list_head list; /* scsi_cmnd participates in queue lists */ struct list_head list; /* scsi_cmnd participates in queue lists */
struct list_head eh_entry; /* entry for the host eh_cmd_q */ struct list_head eh_entry; /* entry for the host eh_cmd_q */
struct delayed_work abort_work; struct delayed_work abort_work;
struct rcu_head rcu;
int eh_eflags; /* Used by error handlr */ int eh_eflags; /* Used by error handlr */
/* /*
......
...@@ -571,8 +571,6 @@ struct Scsi_Host { ...@@ -571,8 +571,6 @@ struct Scsi_Host {
struct blk_mq_tag_set tag_set; struct blk_mq_tag_set tag_set;
}; };
struct rcu_head rcu;
atomic_t host_busy; /* commands actually active on low-level */ atomic_t host_busy; /* commands actually active on low-level */
atomic_t host_blocked; atomic_t host_blocked;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment