aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c45
1 files changed, 24 insertions, 21 deletions
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 29429a13fd90..def9e6b38459 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -441,18 +441,28 @@ static void srp_disconnect_target(struct srp_target_port *target)
441 wait_for_completion(&target->done); 441 wait_for_completion(&target->done);
442} 442}
443 443
444static bool srp_change_state(struct srp_target_port *target,
445 enum srp_target_state old,
446 enum srp_target_state new)
447{
448 bool changed = false;
449
450 spin_lock_irq(target->scsi_host->host_lock);
451 if (target->state == old) {
452 target->state = new;
453 changed = true;
454 }
455 spin_unlock_irq(target->scsi_host->host_lock);
456 return changed;
457}
458
444static void srp_remove_work(struct work_struct *work) 459static void srp_remove_work(struct work_struct *work)
445{ 460{
446 struct srp_target_port *target = 461 struct srp_target_port *target =
447 container_of(work, struct srp_target_port, work); 462 container_of(work, struct srp_target_port, work);
448 463
449 spin_lock_irq(target->scsi_host->host_lock); 464 if (!srp_change_state(target, SRP_TARGET_DEAD, SRP_TARGET_REMOVED))
450 if (target->state != SRP_TARGET_DEAD) {
451 spin_unlock_irq(target->scsi_host->host_lock);
452 return; 465 return;
453 }
454 target->state = SRP_TARGET_REMOVED;
455 spin_unlock_irq(target->scsi_host->host_lock);
456 466
457 spin_lock(&target->srp_host->target_lock); 467 spin_lock(&target->srp_host->target_lock);
458 list_del(&target->list); 468 list_del(&target->list);
@@ -560,13 +570,8 @@ static int srp_reconnect_target(struct srp_target_port *target)
560 struct ib_wc wc; 570 struct ib_wc wc;
561 int ret; 571 int ret;
562 572
563 spin_lock_irq(target->scsi_host->host_lock); 573 if (!srp_change_state(target, SRP_TARGET_LIVE, SRP_TARGET_CONNECTING))
564 if (target->state != SRP_TARGET_LIVE) {
565 spin_unlock_irq(target->scsi_host->host_lock);
566 return -EAGAIN; 574 return -EAGAIN;
567 }
568 target->state = SRP_TARGET_CONNECTING;
569 spin_unlock_irq(target->scsi_host->host_lock);
570 575
571 srp_disconnect_target(target); 576 srp_disconnect_target(target);
572 /* 577 /*
@@ -605,13 +610,8 @@ static int srp_reconnect_target(struct srp_target_port *target)
605 if (ret) 610 if (ret)
606 goto err; 611 goto err;
607 612
608 spin_lock_irq(target->scsi_host->host_lock); 613 if (!srp_change_state(target, SRP_TARGET_CONNECTING, SRP_TARGET_LIVE))
609 if (target->state == SRP_TARGET_CONNECTING) {
610 ret = 0;
611 target->state = SRP_TARGET_LIVE;
612 } else
613 ret = -EAGAIN; 614 ret = -EAGAIN;
614 spin_unlock_irq(target->scsi_host->host_lock);
615 615
616 return ret; 616 return ret;
617 617
@@ -621,9 +621,12 @@ err:
621 621
622 /* 622 /*
623 * We couldn't reconnect, so kill our target port off. 623 * We couldn't reconnect, so kill our target port off.
624 * However, we have to defer the real removal because we might 624 * However, we have to defer the real removal because we
625 * be in the context of the SCSI error handler now, which 625 * are in the context of the SCSI error handler now, which
626 * would deadlock if we call scsi_remove_host(). 626 * will deadlock if we call scsi_remove_host().
627 *
628 * Schedule our work inside the lock to avoid a race with
629 * the flush_scheduled_work() in srp_remove_one().
627 */ 630 */
628 spin_lock_irq(target->scsi_host->host_lock); 631 spin_lock_irq(target->scsi_host->host_lock);
629 if (target->state == SRP_TARGET_CONNECTING) { 632 if (target->state == SRP_TARGET_CONNECTING) {