Patch "scsi: core: Ensure that the SCSI error handler gets woken up" has been added to the 4.14-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
scsi: core: Ensure that the SCSI error handler gets woken up
to the 4.14-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
scsi-core-ensure-that-the-scsi-error-handler-gets-woken-up.patch
and it can be found in the queue-4.14 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
3bd6f43f5cb3714f70c591514f344389df593501Mon Sep 17 00:00:00 2001
From: Bart Van Assche <[email protected]>
Date: Mon, 4 Dec 2017 10:06:23 -0800
Subject: scsi: core: Ensure that the SCSI error handler gets woken up
From: Bart Van Assche <[email protected]>
commit 3bd6f43f5cb3714f70c591514f344389df593501 upstream.
If scsi_eh_scmd_add() is called concurrently with scsi_host_queue_ready() while shost->host_blocked > 0 then it can happen that neither function wakes up the SCSI error handler. Fix this by making every function that decreases the host_busy counter wake up the error handler if necessary and by protecting the host_failed checks with the SCSI host lock.
References: https://marc.info/?l=linux-kernel&m=150461610630736
--- drivers/scsi/hosts.c | 6 ++++++ drivers/scsi/scsi_error.c | 18 ++++++++++++++++-- drivers/scsi/scsi_lib.c | 39 ++++++++++++++++++++++++++++----------- include/scsi/scsi_host.h | 2 ++ 4 files changed, 52 insertions(+), 13 deletions(-)
drivers/scsi/hosts.c +6 -0
@@ -318,6 +318,9 @@ static void scsi_host_dev_release(structscsi_proc_hostdir_rm (shost->hostt); +/* Wait for functions invoked through call_rcu(&shost->rcu, ...) */ +rcu_barrier (); +if (shost->tmf_work_q)destroy_workqueue (shost->tmf_work_q);if (shost->ehandler)
@@ -325,6 +328,8 @@ static void scsi_host_dev_release(structif (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()
@@ -399,6 +404,7 @@ struct Scsi_Host *scsi_host_alloc(structINIT_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 )
drivers/scsi/scsi_error.c +16 -2
@@ -220,6 +220,17 @@ static void scsi_eh_reset(struct scsi_cm } } +static void scsi_eh_inc_host_failed (struct rcu_head *head) +{ +struct Scsi_Host *shost =container_of (head,typeof (*shost), rcu); +unsigned long flags; + +spin_lock_irqsave (shost->host_lock, flags); + shost->host_failed++; +scsi_eh_wakeup (shost); +spin_unlock_irqrestore (shost->host_lock, flags); +} +/** * scsi_eh_scmd_add - add scsi cmd to error handling. * @scmd: scmd to run eh on.
@@ -242,9 +253,12 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scsi_eh_reset (scmd);list_add_tail (&scmd->eh_entry, &shost->eh_cmd_q); - shost->host_failed++; -scsi_eh_wakeup (shost);spin_unlock_irqrestore (shost->host_lock, flags); +/* +* Ensure that all tasks observe the host state change before the +* host_failed change. +*/ +call_rcu (&shost->rcu, scsi_eh_inc_host_failed); }/**
drivers/scsi/scsi_lib.c +28 -11
@@ -318,22 +318,39 @@ static void scsi_init_cmd_errh(struct sc cmd->cmd_len =scsi_command_size (cmd->cmnd); } -void scsi_device_unbusy (struct scsi_device *sdev) +/* +* Decrement the host_busy counter and wake up the error handler if necessary. +* Avoid as follows that the error handler is not woken up if shost->host_busy +* == shost->host_failed: use call_rcu() in scsi_eh_scmd_add() in combination +* with an RCU read lock in this function to ensure that this function in its +* entirety either finishes before scsi_eh_scmd_add() increases the +* host_failed counter or that it notices the shost state change made by +* scsi_eh_scmd_add(). +*/ +static void scsi_dec_host_busy (struct Scsi_Host *shost) { -struct Scsi_Host *shost = sdev->host; -struct scsi_target *starget =scsi_target (sdev);unsigned long flags; +rcu_read_lock ();atomic_dec (&shost->host_busy); -if (starget->can_queue >0 ) -atomic_dec (&starget->target_busy); - -if (unlikely (scsi_host_in_recovery (shost) && - (shost->host_failed || shost->host_eh_scheduled))) { +if (unlikely (scsi_host_in_recovery (shost))) {spin_lock_irqsave (shost->host_lock, flags); -scsi_eh_wakeup (shost); +if (shost->host_failed || shost->host_eh_scheduled) +scsi_eh_wakeup (shost);spin_unlock_irqrestore (shost->host_lock, flags); } +rcu_read_unlock (); +} + +void scsi_device_unbusy (struct scsi_device *sdev) +{ +struct Scsi_Host *shost = sdev->host; +struct scsi_target *starget =scsi_target (sdev); + +scsi_dec_host_busy (shost); + +if (starget->can_queue >0 ) +atomic_dec (&starget->target_busy);atomic_dec (&sdev->device_busy); }
@@ -1532,7 +1549,7 @@ starved:list_add_tail (&sdev->starved_entry, &shost->starved_list);spin_unlock_irq (shost->host_lock); out_dec: -atomic_dec (&shost->host_busy); +scsi_dec_host_busy (shost);return 0 ; }
@@ -1993,7 +2010,7 @@ static blk_status_t scsi_queue_rq(structreturn BLK_STS_OK ; out_dec_host_busy: -atomic_dec (&shost->host_busy); +scsi_dec_host_busy (shost); out_dec_target_busy:if (scsi_target (sdev)->can_queue >0 )atomic_dec (&scsi_target (sdev)->target_busy);
include/scsi/scsi_host.h +2 -0
@@ -571,6 +571,8 @@ 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;
Patches currently in stable-queue which might be from [email protected] are
queue-4.14/scsi-core-ensure-that-the-scsi-error-handler-gets-woken-up.patch queue-4.14/pktcdvd-fix-pkt_setup_dev-error-path.patch queue-4.14/pktcdvd-fix-a-recently-introduced-null-pointer-dereference.patch