diff options
author | Wen Xiong <wenxiong@linux.vnet.ibm.com> | 2018-09-20 20:32:12 -0400 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2018-09-21 12:35:39 -0400 |
commit | 318ddb34b2052f838aa243d07173e2badf3e630e (patch) | |
tree | 8ea8c5955e10590caa7951a9d39c3e04d04b3b2f | |
parent | 8c39e2699f8acb2e29782a834e56306da24937fe (diff) |
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 <wenxiong@linux.vnet.ibm.com>
Acked-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r-- | drivers/scsi/ipr.c | 106 | ||||
-rw-r--r-- | drivers/scsi/ipr.h | 1 |
2 files changed, 62 insertions, 45 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,6 +3335,65 @@ static void ipr_release_dump(struct kref *kref) | |||
3335 | LEAVE; | 3335 | LEAVE; |
3336 | } | 3336 | } |
3337 | 3337 | ||
3338 | static void ipr_add_remove_thread(struct work_struct *work) | ||
3339 | { | ||
3340 | unsigned long lock_flags; | ||
3341 | struct ipr_resource_entry *res; | ||
3342 | struct scsi_device *sdev; | ||
3343 | struct ipr_ioa_cfg *ioa_cfg = | ||
3344 | container_of(work, struct ipr_ioa_cfg, scsi_add_work_q); | ||
3345 | u8 bus, target, lun; | ||
3346 | int did_work; | ||
3347 | |||
3348 | ENTER; | ||
3349 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); | ||
3350 | |||
3351 | restart: | ||
3352 | do { | ||
3353 | did_work = 0; | ||
3354 | if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) { | ||
3355 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3356 | return; | ||
3357 | } | ||
3358 | |||
3359 | list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { | ||
3360 | if (res->del_from_ml && res->sdev) { | ||
3361 | did_work = 1; | ||
3362 | sdev = res->sdev; | ||
3363 | if (!scsi_device_get(sdev)) { | ||
3364 | if (!res->add_to_ml) | ||
3365 | list_move_tail(&res->queue, &ioa_cfg->free_res_q); | ||
3366 | else | ||
3367 | res->del_from_ml = 0; | ||
3368 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3369 | scsi_remove_device(sdev); | ||
3370 | scsi_device_put(sdev); | ||
3371 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); | ||
3372 | } | ||
3373 | break; | ||
3374 | } | ||
3375 | } | ||
3376 | } while (did_work); | ||
3377 | |||
3378 | list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { | ||
3379 | if (res->add_to_ml) { | ||
3380 | bus = res->bus; | ||
3381 | target = res->target; | ||
3382 | lun = res->lun; | ||
3383 | res->add_to_ml = 0; | ||
3384 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3385 | scsi_add_device(ioa_cfg->host, bus, target, lun); | ||
3386 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); | ||
3387 | goto restart; | ||
3388 | } | ||
3389 | } | ||
3390 | |||
3391 | ioa_cfg->scan_done = 1; | ||
3392 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3393 | kobject_uevent(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE); | ||
3394 | LEAVE; | ||
3395 | } | ||
3396 | |||
3338 | /** | 3397 | /** |
3339 | * ipr_worker_thread - Worker thread | 3398 | * ipr_worker_thread - Worker thread |
3340 | * @work: ioa config struct | 3399 | * @work: ioa config struct |
@@ -3349,13 +3408,9 @@ static void ipr_release_dump(struct kref *kref) | |||
3349 | static void ipr_worker_thread(struct work_struct *work) | 3408 | static void ipr_worker_thread(struct work_struct *work) |
3350 | { | 3409 | { |
3351 | unsigned long lock_flags; | 3410 | unsigned long lock_flags; |
3352 | struct ipr_resource_entry *res; | ||
3353 | struct scsi_device *sdev; | ||
3354 | struct ipr_dump *dump; | 3411 | struct ipr_dump *dump; |
3355 | struct ipr_ioa_cfg *ioa_cfg = | 3412 | struct ipr_ioa_cfg *ioa_cfg = |
3356 | container_of(work, struct ipr_ioa_cfg, work_q); | 3413 | container_of(work, struct ipr_ioa_cfg, work_q); |
3357 | u8 bus, target, lun; | ||
3358 | int did_work; | ||
3359 | 3414 | ||
3360 | ENTER; | 3415 | ENTER; |
3361 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); | 3416 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); |
@@ -3393,49 +3448,9 @@ static void ipr_worker_thread(struct work_struct *work) | |||
3393 | return; | 3448 | return; |
3394 | } | 3449 | } |
3395 | 3450 | ||
3396 | restart: | 3451 | schedule_work(&ioa_cfg->scsi_add_work_q); |
3397 | do { | ||
3398 | did_work = 0; | ||
3399 | if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) { | ||
3400 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3401 | return; | ||
3402 | } | ||
3403 | 3452 | ||
3404 | list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { | ||
3405 | if (res->del_from_ml && res->sdev) { | ||
3406 | did_work = 1; | ||
3407 | sdev = res->sdev; | ||
3408 | if (!scsi_device_get(sdev)) { | ||
3409 | if (!res->add_to_ml) | ||
3410 | list_move_tail(&res->queue, &ioa_cfg->free_res_q); | ||
3411 | else | ||
3412 | res->del_from_ml = 0; | ||
3413 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3414 | scsi_remove_device(sdev); | ||
3415 | scsi_device_put(sdev); | ||
3416 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); | ||
3417 | } | ||
3418 | break; | ||
3419 | } | ||
3420 | } | ||
3421 | } while (did_work); | ||
3422 | |||
3423 | list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { | ||
3424 | if (res->add_to_ml) { | ||
3425 | bus = res->bus; | ||
3426 | target = res->target; | ||
3427 | lun = res->lun; | ||
3428 | res->add_to_ml = 0; | ||
3429 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | ||
3430 | scsi_add_device(ioa_cfg->host, bus, target, lun); | ||
3431 | spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); | ||
3432 | goto restart; | ||
3433 | } | ||
3434 | } | ||
3435 | |||
3436 | ioa_cfg->scan_done = 1; | ||
3437 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); | 3453 | spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); |
3438 | kobject_uevent(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE); | ||
3439 | LEAVE; | 3454 | LEAVE; |
3440 | } | 3455 | } |
3441 | 3456 | ||
@@ -9933,6 +9948,7 @@ static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, | |||
9933 | INIT_LIST_HEAD(&ioa_cfg->free_res_q); | 9948 | INIT_LIST_HEAD(&ioa_cfg->free_res_q); |
9934 | INIT_LIST_HEAD(&ioa_cfg->used_res_q); | 9949 | INIT_LIST_HEAD(&ioa_cfg->used_res_q); |
9935 | INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); | 9950 | INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); |
9951 | INIT_WORK(&ioa_cfg->scsi_add_work_q, ipr_add_remove_thread); | ||
9936 | init_waitqueue_head(&ioa_cfg->reset_wait_q); | 9952 | init_waitqueue_head(&ioa_cfg->reset_wait_q); |
9937 | init_waitqueue_head(&ioa_cfg->msi_wait_q); | 9953 | init_waitqueue_head(&ioa_cfg->msi_wait_q); |
9938 | init_waitqueue_head(&ioa_cfg->eeh_wait_q); | 9954 | 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 { | |||
1575 | u8 saved_mode_page_len; | 1575 | u8 saved_mode_page_len; |
1576 | 1576 | ||
1577 | struct work_struct work_q; | 1577 | struct work_struct work_q; |
1578 | struct work_struct scsi_add_work_q; | ||
1578 | struct workqueue_struct *reset_work_q; | 1579 | struct workqueue_struct *reset_work_q; |
1579 | 1580 | ||
1580 | wait_queue_head_t reset_wait_q; | 1581 | wait_queue_head_t reset_wait_q; |