diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/infiniband/ulp/srp/ib_srp.c | 45 |
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 | ||
444 | static 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 | |||
444 | static void srp_remove_work(struct work_struct *work) | 459 | static 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) { |