From 318ddb34b2052f838aa243d07173e2badf3e630e Mon Sep 17 00:00:00 2001 From: Wen Xiong Date: Thu, 20 Sep 2018 19:32:12 -0500 Subject: [PATCH] scsi: ipr: System hung while dlpar adding primary ipr adapter back While dlpar adding primary ipr adapter back, driver goes through adapter initialization then schedule ipr_worker_thread to start te disk scan by dropping the host lock, calling scsi_add_device. Then get the adapter reset request again, so driver does scsi_block_requests, this will cause the scsi_add_device get hung until we unblock. But we can't run ipr_worker_thread to do the unblock because its stuck in scsi_add_device. This patch fixes the issue. [mkp: typo and whitespace fixes] Signed-off-by: Wen Xiong Acked-by: Brian King Signed-off-by: Martin K. Petersen --- drivers/scsi/ipr.c | 110 ++++++++++++++++++++++++++------------------- drivers/scsi/ipr.h | 1 + 2 files changed, 64 insertions(+), 47 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index f2ec80b0ffc0..271990bc065b 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3335,64 +3335,19 @@ static void ipr_release_dump(struct kref *kref) LEAVE; } -/** - * ipr_worker_thread - Worker thread - * @work: ioa config struct - * - * Called at task level from a work thread. This function takes care - * of adding and removing device from the mid-layer as configuration - * changes are detected by the adapter. - * - * Return value: - * nothing - **/ -static void ipr_worker_thread(struct work_struct *work) +static void ipr_add_remove_thread(struct work_struct *work) { unsigned long lock_flags; struct ipr_resource_entry *res; struct scsi_device *sdev; - struct ipr_dump *dump; struct ipr_ioa_cfg *ioa_cfg = - container_of(work, struct ipr_ioa_cfg, work_q); + container_of(work, struct ipr_ioa_cfg, scsi_add_work_q); u8 bus, target, lun; int did_work; ENTER; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - if (ioa_cfg->sdt_state == READ_DUMP) { - dump = ioa_cfg->dump; - if (!dump) { - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - return; - } - kref_get(&dump->kref); - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - ipr_get_ioa_dump(ioa_cfg, dump); - kref_put(&dump->kref, ipr_release_dump); - - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - if (ioa_cfg->sdt_state == DUMP_OBTAINED && !ioa_cfg->dump_timeout) - ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - return; - } - - if (ioa_cfg->scsi_unblock) { - ioa_cfg->scsi_unblock = 0; - ioa_cfg->scsi_blocked = 0; - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - scsi_unblock_requests(ioa_cfg->host); - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - if (ioa_cfg->scsi_blocked) - scsi_block_requests(ioa_cfg->host); - } - - if (!ioa_cfg->scan_enabled) { - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - return; - } - restart: do { did_work = 0; @@ -3439,6 +3394,66 @@ restart: LEAVE; } +/** + * ipr_worker_thread - Worker thread + * @work: ioa config struct + * + * Called at task level from a work thread. This function takes care + * of adding and removing device from the mid-layer as configuration + * changes are detected by the adapter. + * + * Return value: + * nothing + **/ +static void ipr_worker_thread(struct work_struct *work) +{ + unsigned long lock_flags; + struct ipr_dump *dump; + struct ipr_ioa_cfg *ioa_cfg = + container_of(work, struct ipr_ioa_cfg, work_q); + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (ioa_cfg->sdt_state == READ_DUMP) { + dump = ioa_cfg->dump; + if (!dump) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + kref_get(&dump->kref); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + ipr_get_ioa_dump(ioa_cfg, dump); + kref_put(&dump->kref, ipr_release_dump); + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ioa_cfg->sdt_state == DUMP_OBTAINED && !ioa_cfg->dump_timeout) + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + if (ioa_cfg->scsi_unblock) { + ioa_cfg->scsi_unblock = 0; + ioa_cfg->scsi_blocked = 0; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_unblock_requests(ioa_cfg->host); + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ioa_cfg->scsi_blocked) + scsi_block_requests(ioa_cfg->host); + } + + if (!ioa_cfg->scan_enabled) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + schedule_work(&ioa_cfg->scsi_add_work_q); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + LEAVE; +} + #ifdef CONFIG_SCSI_IPR_TRACE /** * ipr_read_trace - Dump the adapter trace @@ -9933,6 +9948,7 @@ static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, INIT_LIST_HEAD(&ioa_cfg->free_res_q); INIT_LIST_HEAD(&ioa_cfg->used_res_q); INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); + INIT_WORK(&ioa_cfg->scsi_add_work_q, ipr_add_remove_thread); init_waitqueue_head(&ioa_cfg->reset_wait_q); init_waitqueue_head(&ioa_cfg->msi_wait_q); init_waitqueue_head(&ioa_cfg->eeh_wait_q); diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 68afbbde54d3..f6baa2351313 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1575,6 +1575,7 @@ struct ipr_ioa_cfg { u8 saved_mode_page_len; struct work_struct work_q; + struct work_struct scsi_add_work_q; struct workqueue_struct *reset_work_q; wait_queue_head_t reset_wait_q;