FAILED: patch "[PATCH] scsi: core: Ensure that the SCSI error handler gets woken up" failed to apply to 3.18-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 3.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
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: [PATCH] scsi: core: Ensure that the SCSI error handler gets woken up
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 -0
@@ -318,6 +318,9 @@ static void scsi_host_dev_release(struct device *dev)scsi_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(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()
@@ -399,6 +404,7 @@ 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 )
drivers/scsi/scsi_error.c +16 -2
@@ -220,6 +220,17 @@ 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); +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 *scmd)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 scsi_cmnd *cmd) 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 @@ static inline int scsi_host_queue_ready(struct request_queue *q,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 ; }
@@ -2018,7 +2035,7 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx,return 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;