diff options
-rw-r--r-- | drivers/infiniband/ulp/srp/ib_srp.c | 83 | ||||
-rw-r--r-- | drivers/infiniband/ulp/srp/ib_srp.h | 5 |
2 files changed, 32 insertions, 56 deletions
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index beb68786001e..95590a38e88a 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c | |||
@@ -428,6 +428,23 @@ static int srp_send_req(struct srp_target_port *target) | |||
428 | return status; | 428 | return status; |
429 | } | 429 | } |
430 | 430 | ||
431 | static bool srp_queue_remove_work(struct srp_target_port *target) | ||
432 | { | ||
433 | bool changed = false; | ||
434 | |||
435 | spin_lock_irq(&target->lock); | ||
436 | if (target->state != SRP_TARGET_REMOVED) { | ||
437 | target->state = SRP_TARGET_REMOVED; | ||
438 | changed = true; | ||
439 | } | ||
440 | spin_unlock_irq(&target->lock); | ||
441 | |||
442 | if (changed) | ||
443 | queue_work(system_long_wq, &target->remove_work); | ||
444 | |||
445 | return changed; | ||
446 | } | ||
447 | |||
431 | static bool srp_change_conn_state(struct srp_target_port *target, | 448 | static bool srp_change_conn_state(struct srp_target_port *target, |
432 | bool connected) | 449 | bool connected) |
433 | { | 450 | { |
@@ -458,21 +475,6 @@ static void srp_disconnect_target(struct srp_target_port *target) | |||
458 | } | 475 | } |
459 | } | 476 | } |
460 | 477 | ||
461 | static bool srp_change_state(struct srp_target_port *target, | ||
462 | enum srp_target_state old, | ||
463 | enum srp_target_state new) | ||
464 | { | ||
465 | bool changed = false; | ||
466 | |||
467 | spin_lock_irq(&target->lock); | ||
468 | if (target->state == old) { | ||
469 | target->state = new; | ||
470 | changed = true; | ||
471 | } | ||
472 | spin_unlock_irq(&target->lock); | ||
473 | return changed; | ||
474 | } | ||
475 | |||
476 | static void srp_free_req_data(struct srp_target_port *target) | 478 | static void srp_free_req_data(struct srp_target_port *target) |
477 | { | 479 | { |
478 | struct ib_device *ibdev = target->srp_host->srp_dev->dev; | 480 | struct ib_device *ibdev = target->srp_host->srp_dev->dev; |
@@ -508,9 +510,12 @@ static void srp_del_scsi_host_attr(struct Scsi_Host *shost) | |||
508 | 510 | ||
509 | static void srp_remove_target(struct srp_target_port *target) | 511 | static void srp_remove_target(struct srp_target_port *target) |
510 | { | 512 | { |
513 | WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED); | ||
514 | |||
511 | srp_del_scsi_host_attr(target->scsi_host); | 515 | srp_del_scsi_host_attr(target->scsi_host); |
512 | srp_remove_host(target->scsi_host); | 516 | srp_remove_host(target->scsi_host); |
513 | scsi_remove_host(target->scsi_host); | 517 | scsi_remove_host(target->scsi_host); |
518 | srp_disconnect_target(target); | ||
514 | ib_destroy_cm_id(target->cm_id); | 519 | ib_destroy_cm_id(target->cm_id); |
515 | srp_free_target_ib(target); | 520 | srp_free_target_ib(target); |
516 | srp_free_req_data(target); | 521 | srp_free_req_data(target); |
@@ -520,10 +525,9 @@ static void srp_remove_target(struct srp_target_port *target) | |||
520 | static void srp_remove_work(struct work_struct *work) | 525 | static void srp_remove_work(struct work_struct *work) |
521 | { | 526 | { |
522 | struct srp_target_port *target = | 527 | struct srp_target_port *target = |
523 | container_of(work, struct srp_target_port, work); | 528 | container_of(work, struct srp_target_port, remove_work); |
524 | 529 | ||
525 | if (!srp_change_state(target, SRP_TARGET_DEAD, SRP_TARGET_REMOVED)) | 530 | WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED); |
526 | return; | ||
527 | 531 | ||
528 | spin_lock(&target->srp_host->target_lock); | 532 | spin_lock(&target->srp_host->target_lock); |
529 | list_del(&target->list); | 533 | list_del(&target->list); |
@@ -738,17 +742,8 @@ err: | |||
738 | * However, we have to defer the real removal because we | 742 | * However, we have to defer the real removal because we |
739 | * are in the context of the SCSI error handler now, which | 743 | * are in the context of the SCSI error handler now, which |
740 | * will deadlock if we call scsi_remove_host(). | 744 | * will deadlock if we call scsi_remove_host(). |
741 | * | ||
742 | * Schedule our work inside the lock to avoid a race with | ||
743 | * the flush_scheduled_work() in srp_remove_one(). | ||
744 | */ | 745 | */ |
745 | spin_lock_irq(&target->lock); | 746 | srp_queue_remove_work(target); |
746 | if (target->state == SRP_TARGET_LIVE) { | ||
747 | target->state = SRP_TARGET_DEAD; | ||
748 | INIT_WORK(&target->work, srp_remove_work); | ||
749 | queue_work(ib_wq, &target->work); | ||
750 | } | ||
751 | spin_unlock_irq(&target->lock); | ||
752 | 747 | ||
753 | return ret; | 748 | return ret; |
754 | } | 749 | } |
@@ -2258,6 +2253,7 @@ static ssize_t srp_create_target(struct device *dev, | |||
2258 | sizeof (struct srp_indirect_buf) + | 2253 | sizeof (struct srp_indirect_buf) + |
2259 | target->cmd_sg_cnt * sizeof (struct srp_direct_buf); | 2254 | target->cmd_sg_cnt * sizeof (struct srp_direct_buf); |
2260 | 2255 | ||
2256 | INIT_WORK(&target->remove_work, srp_remove_work); | ||
2261 | spin_lock_init(&target->lock); | 2257 | spin_lock_init(&target->lock); |
2262 | INIT_LIST_HEAD(&target->free_tx); | 2258 | INIT_LIST_HEAD(&target->free_tx); |
2263 | INIT_LIST_HEAD(&target->free_reqs); | 2259 | INIT_LIST_HEAD(&target->free_reqs); |
@@ -2491,8 +2487,7 @@ static void srp_remove_one(struct ib_device *device) | |||
2491 | { | 2487 | { |
2492 | struct srp_device *srp_dev; | 2488 | struct srp_device *srp_dev; |
2493 | struct srp_host *host, *tmp_host; | 2489 | struct srp_host *host, *tmp_host; |
2494 | LIST_HEAD(target_list); | 2490 | struct srp_target_port *target; |
2495 | struct srp_target_port *target, *tmp_target; | ||
2496 | 2491 | ||
2497 | srp_dev = ib_get_client_data(device, &srp_client); | 2492 | srp_dev = ib_get_client_data(device, &srp_client); |
2498 | 2493 | ||
@@ -2505,35 +2500,17 @@ static void srp_remove_one(struct ib_device *device) | |||
2505 | wait_for_completion(&host->released); | 2500 | wait_for_completion(&host->released); |
2506 | 2501 | ||
2507 | /* | 2502 | /* |
2508 | * Mark all target ports as removed, so we stop queueing | 2503 | * Remove all target ports. |
2509 | * commands and don't try to reconnect. | ||
2510 | */ | 2504 | */ |
2511 | spin_lock(&host->target_lock); | 2505 | spin_lock(&host->target_lock); |
2512 | list_for_each_entry(target, &host->target_list, list) { | 2506 | list_for_each_entry(target, &host->target_list, list) |
2513 | spin_lock_irq(&target->lock); | 2507 | srp_queue_remove_work(target); |
2514 | target->state = SRP_TARGET_REMOVED; | ||
2515 | spin_unlock_irq(&target->lock); | ||
2516 | } | ||
2517 | spin_unlock(&host->target_lock); | 2508 | spin_unlock(&host->target_lock); |
2518 | 2509 | ||
2519 | /* | 2510 | /* |
2520 | * Wait for any reconnection tasks that may have | 2511 | * Wait for target port removal tasks. |
2521 | * started before we marked our target ports as | ||
2522 | * removed, and any target port removal tasks. | ||
2523 | */ | 2512 | */ |
2524 | flush_workqueue(ib_wq); | 2513 | flush_workqueue(system_long_wq); |
2525 | |||
2526 | list_for_each_entry_safe(target, tmp_target, | ||
2527 | &host->target_list, list) { | ||
2528 | srp_del_scsi_host_attr(target->scsi_host); | ||
2529 | srp_remove_host(target->scsi_host); | ||
2530 | scsi_remove_host(target->scsi_host); | ||
2531 | srp_disconnect_target(target); | ||
2532 | ib_destroy_cm_id(target->cm_id); | ||
2533 | srp_free_target_ib(target); | ||
2534 | srp_free_req_data(target); | ||
2535 | scsi_host_put(target->scsi_host); | ||
2536 | } | ||
2537 | 2514 | ||
2538 | kfree(host); | 2515 | kfree(host); |
2539 | } | 2516 | } |
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index ef95fa4ca3ef..de2d0b3c0bfe 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h | |||
@@ -80,8 +80,7 @@ enum { | |||
80 | 80 | ||
81 | enum srp_target_state { | 81 | enum srp_target_state { |
82 | SRP_TARGET_LIVE, | 82 | SRP_TARGET_LIVE, |
83 | SRP_TARGET_DEAD, | 83 | SRP_TARGET_REMOVED, |
84 | SRP_TARGET_REMOVED | ||
85 | }; | 84 | }; |
86 | 85 | ||
87 | enum srp_iu_type { | 86 | enum srp_iu_type { |
@@ -175,7 +174,7 @@ struct srp_target_port { | |||
175 | struct srp_iu *rx_ring[SRP_RQ_SIZE]; | 174 | struct srp_iu *rx_ring[SRP_RQ_SIZE]; |
176 | struct srp_request req_ring[SRP_CMD_SQ_SIZE]; | 175 | struct srp_request req_ring[SRP_CMD_SQ_SIZE]; |
177 | 176 | ||
178 | struct work_struct work; | 177 | struct work_struct remove_work; |
179 | 178 | ||
180 | struct list_head list; | 179 | struct list_head list; |
181 | struct completion done; | 180 | struct completion done; |