Skip to content

Commit

Permalink
scsi: ipr: System hung while dlpar adding primary ipr adapter back
Browse files Browse the repository at this point in the history
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 <wenxiong@linux.vnet.ibm.com>
Acked-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Wen Xiong authored and Martin K. Petersen committed Sep 21, 2018
1 parent 8c39e26 commit 318ddb3
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 45 deletions.
106 changes: 61 additions & 45 deletions drivers/scsi/ipr.c
Original file line number Diff line number Diff line change
Expand Up @@ -3335,6 +3335,65 @@ static void ipr_release_dump(struct kref *kref)
LEAVE;
}

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_ioa_cfg *ioa_cfg =
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);

restart:
do {
did_work = 0;
if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return;
}

list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->del_from_ml && res->sdev) {
did_work = 1;
sdev = res->sdev;
if (!scsi_device_get(sdev)) {
if (!res->add_to_ml)
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
else
res->del_from_ml = 0;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_remove_device(sdev);
scsi_device_put(sdev);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
}
break;
}
}
} while (did_work);

list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->add_to_ml) {
bus = res->bus;
target = res->target;
lun = res->lun;
res->add_to_ml = 0;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_add_device(ioa_cfg->host, bus, target, lun);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
goto restart;
}
}

ioa_cfg->scan_done = 1;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
kobject_uevent(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE);
LEAVE;
}

/**
* ipr_worker_thread - Worker thread
* @work: ioa config struct
Expand All @@ -3349,13 +3408,9 @@ static void ipr_release_dump(struct kref *kref)
static void ipr_worker_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);
u8 bus, target, lun;
int did_work;

ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
Expand Down Expand Up @@ -3393,49 +3448,9 @@ static void ipr_worker_thread(struct work_struct *work)
return;
}

restart:
do {
did_work = 0;
if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return;
}
schedule_work(&ioa_cfg->scsi_add_work_q);

list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->del_from_ml && res->sdev) {
did_work = 1;
sdev = res->sdev;
if (!scsi_device_get(sdev)) {
if (!res->add_to_ml)
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
else
res->del_from_ml = 0;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_remove_device(sdev);
scsi_device_put(sdev);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
}
break;
}
}
} while (did_work);

list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->add_to_ml) {
bus = res->bus;
target = res->target;
lun = res->lun;
res->add_to_ml = 0;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_add_device(ioa_cfg->host, bus, target, lun);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
goto restart;
}
}

ioa_cfg->scan_done = 1;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
kobject_uevent(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE);
LEAVE;
}

Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions drivers/scsi/ipr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 318ddb3

Please sign in to comment.