Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 318ddb34 authored by Wen Xiong's avatar Wen Xiong Committed by Martin K. Petersen
Browse files

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: default avatarWen Xiong <wenxiong@linux.vnet.ibm.com>
Acked-by: default avatarBrian King <brking@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 8c39e269
Loading
Loading
Loading
Loading
+61 −45
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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;
}

@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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;