diff options
author | Ariel Nahum <arieln@mellanox.com> | 2014-05-22 04:00:18 -0400 |
---|---|---|
committer | Roland Dreier <roland@purestorage.com> | 2014-05-26 11:19:48 -0400 |
commit | b73c3adabdb1e2cb2f2c69bc3cbb9306aa3f9700 (patch) | |
tree | 4450f306e659387ffc2ec291a611c89ded989b06 /drivers/infiniband/ulp/iser/iser_verbs.c | |
parent | d6d211db37e75de2ddc3a4f979038c40df7cc79c (diff) |
IB/iser: Simplify connection management
iSER relies on refcounting to manage iser connections establishment
and teardown.
Following commit 39ff05dbbbdb ("IB/iser: Enhance disconnection logic
for multi-pathing"), iser connection maintain 3 references:
- iscsi_endpoint (at creation stage)
- cma_id (at connection request stage)
- iscsi_conn (at bind stage)
We can avoid taking explicit refcounts by correctly serializing iser
teardown flows (graceful and non-graceful).
Our approach is to trigger a scheduled work to handle ordered teardown
by gracefully waiting for 2 cleanup stages to complete:
1. Cleanup of live pending tasks indicated by iscsi_conn_stop completion
2. Flush errors processing
Each completed stage will notify a waiting worker thread when it is
done to allow teardwon continuation.
Since iSCSI connection establishment may trigger endpoint disconnect
without a successful endpoint connect, we rely on the iscsi <-> iser
binding (.conn_bind) to learn about the teardown policy we should take
wrt cleanup stages.
Since all cleanup worker threads are scheduled (release_wq) in
.ep_disconnect it is safe to assume that when module_exit is called,
all cleanup workers are already scheduled. Thus proper module unload
shall flush all scheduled works before allowing safe exit, to
guarantee no resources got left behind.
Signed-off-by: Ariel Nahum <arieln@mellanox.com>
Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Reviewed-by: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband/ulp/iser/iser_verbs.c')
-rw-r--r-- | drivers/infiniband/ulp/iser/iser_verbs.c | 85 |
1 files changed, 37 insertions, 48 deletions
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 32849f2becde..4c698e58e550 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c | |||
@@ -581,14 +581,30 @@ static int iser_conn_state_comp_exch(struct iser_conn *ib_conn, | |||
581 | return ret; | 581 | return ret; |
582 | } | 582 | } |
583 | 583 | ||
584 | void iser_release_work(struct work_struct *work) | ||
585 | { | ||
586 | struct iser_conn *ib_conn; | ||
587 | |||
588 | ib_conn = container_of(work, struct iser_conn, release_work); | ||
589 | |||
590 | /* wait for .conn_stop callback */ | ||
591 | wait_for_completion(&ib_conn->stop_completion); | ||
592 | |||
593 | /* wait for the qp`s post send and post receive buffers to empty */ | ||
594 | wait_event_interruptible(ib_conn->wait, | ||
595 | ib_conn->state == ISER_CONN_DOWN); | ||
596 | |||
597 | iser_conn_release(ib_conn); | ||
598 | } | ||
599 | |||
584 | /** | 600 | /** |
585 | * Frees all conn objects and deallocs conn descriptor | 601 | * Frees all conn objects and deallocs conn descriptor |
586 | */ | 602 | */ |
587 | static void iser_conn_release(struct iser_conn *ib_conn, int can_destroy_id) | 603 | void iser_conn_release(struct iser_conn *ib_conn) |
588 | { | 604 | { |
589 | struct iser_device *device = ib_conn->device; | 605 | struct iser_device *device = ib_conn->device; |
590 | 606 | ||
591 | BUG_ON(ib_conn->state != ISER_CONN_DOWN); | 607 | BUG_ON(ib_conn->state == ISER_CONN_UP); |
592 | 608 | ||
593 | mutex_lock(&ig.connlist_mutex); | 609 | mutex_lock(&ig.connlist_mutex); |
594 | list_del(&ib_conn->conn_list); | 610 | list_del(&ib_conn->conn_list); |
@@ -600,27 +616,13 @@ static void iser_conn_release(struct iser_conn *ib_conn, int can_destroy_id) | |||
600 | if (device != NULL) | 616 | if (device != NULL) |
601 | iser_device_try_release(device); | 617 | iser_device_try_release(device); |
602 | /* if cma handler context, the caller actually destroy the id */ | 618 | /* if cma handler context, the caller actually destroy the id */ |
603 | if (ib_conn->cma_id != NULL && can_destroy_id) { | 619 | if (ib_conn->cma_id != NULL) { |
604 | rdma_destroy_id(ib_conn->cma_id); | 620 | rdma_destroy_id(ib_conn->cma_id); |
605 | ib_conn->cma_id = NULL; | 621 | ib_conn->cma_id = NULL; |
606 | } | 622 | } |
607 | iscsi_destroy_endpoint(ib_conn->ep); | 623 | iscsi_destroy_endpoint(ib_conn->ep); |
608 | } | 624 | } |
609 | 625 | ||
610 | void iser_conn_get(struct iser_conn *ib_conn) | ||
611 | { | ||
612 | atomic_inc(&ib_conn->refcount); | ||
613 | } | ||
614 | |||
615 | int iser_conn_put(struct iser_conn *ib_conn, int can_destroy_id) | ||
616 | { | ||
617 | if (atomic_dec_and_test(&ib_conn->refcount)) { | ||
618 | iser_conn_release(ib_conn, can_destroy_id); | ||
619 | return 1; | ||
620 | } | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | /** | 626 | /** |
625 | * triggers start of the disconnect procedures and wait for them to be done | 627 | * triggers start of the disconnect procedures and wait for them to be done |
626 | */ | 628 | */ |
@@ -638,24 +640,19 @@ void iser_conn_terminate(struct iser_conn *ib_conn) | |||
638 | if (err) | 640 | if (err) |
639 | iser_err("Failed to disconnect, conn: 0x%p err %d\n", | 641 | iser_err("Failed to disconnect, conn: 0x%p err %d\n", |
640 | ib_conn,err); | 642 | ib_conn,err); |
641 | |||
642 | wait_event_interruptible(ib_conn->wait, | ||
643 | ib_conn->state == ISER_CONN_DOWN); | ||
644 | |||
645 | iser_conn_put(ib_conn, 1); /* deref ib conn deallocate */ | ||
646 | } | 643 | } |
647 | 644 | ||
648 | static int iser_connect_error(struct rdma_cm_id *cma_id) | 645 | static void iser_connect_error(struct rdma_cm_id *cma_id) |
649 | { | 646 | { |
650 | struct iser_conn *ib_conn; | 647 | struct iser_conn *ib_conn; |
648 | |||
651 | ib_conn = (struct iser_conn *)cma_id->context; | 649 | ib_conn = (struct iser_conn *)cma_id->context; |
652 | 650 | ||
653 | ib_conn->state = ISER_CONN_DOWN; | 651 | ib_conn->state = ISER_CONN_DOWN; |
654 | wake_up_interruptible(&ib_conn->wait); | 652 | wake_up_interruptible(&ib_conn->wait); |
655 | return iser_conn_put(ib_conn, 0); /* deref ib conn's cma id */ | ||
656 | } | 653 | } |
657 | 654 | ||
658 | static int iser_addr_handler(struct rdma_cm_id *cma_id) | 655 | static void iser_addr_handler(struct rdma_cm_id *cma_id) |
659 | { | 656 | { |
660 | struct iser_device *device; | 657 | struct iser_device *device; |
661 | struct iser_conn *ib_conn; | 658 | struct iser_conn *ib_conn; |
@@ -664,7 +661,8 @@ static int iser_addr_handler(struct rdma_cm_id *cma_id) | |||
664 | device = iser_device_find_by_ib_device(cma_id); | 661 | device = iser_device_find_by_ib_device(cma_id); |
665 | if (!device) { | 662 | if (!device) { |
666 | iser_err("device lookup/creation failed\n"); | 663 | iser_err("device lookup/creation failed\n"); |
667 | return iser_connect_error(cma_id); | 664 | iser_connect_error(cma_id); |
665 | return; | ||
668 | } | 666 | } |
669 | 667 | ||
670 | ib_conn = (struct iser_conn *)cma_id->context; | 668 | ib_conn = (struct iser_conn *)cma_id->context; |
@@ -686,13 +684,12 @@ static int iser_addr_handler(struct rdma_cm_id *cma_id) | |||
686 | ret = rdma_resolve_route(cma_id, 1000); | 684 | ret = rdma_resolve_route(cma_id, 1000); |
687 | if (ret) { | 685 | if (ret) { |
688 | iser_err("resolve route failed: %d\n", ret); | 686 | iser_err("resolve route failed: %d\n", ret); |
689 | return iser_connect_error(cma_id); | 687 | iser_connect_error(cma_id); |
688 | return; | ||
690 | } | 689 | } |
691 | |||
692 | return 0; | ||
693 | } | 690 | } |
694 | 691 | ||
695 | static int iser_route_handler(struct rdma_cm_id *cma_id) | 692 | static void iser_route_handler(struct rdma_cm_id *cma_id) |
696 | { | 693 | { |
697 | struct rdma_conn_param conn_param; | 694 | struct rdma_conn_param conn_param; |
698 | int ret; | 695 | int ret; |
@@ -720,9 +717,9 @@ static int iser_route_handler(struct rdma_cm_id *cma_id) | |||
720 | goto failure; | 717 | goto failure; |
721 | } | 718 | } |
722 | 719 | ||
723 | return 0; | 720 | return; |
724 | failure: | 721 | failure: |
725 | return iser_connect_error(cma_id); | 722 | iser_connect_error(cma_id); |
726 | } | 723 | } |
727 | 724 | ||
728 | static void iser_connected_handler(struct rdma_cm_id *cma_id) | 725 | static void iser_connected_handler(struct rdma_cm_id *cma_id) |
@@ -739,10 +736,9 @@ static void iser_connected_handler(struct rdma_cm_id *cma_id) | |||
739 | wake_up_interruptible(&ib_conn->wait); | 736 | wake_up_interruptible(&ib_conn->wait); |
740 | } | 737 | } |
741 | 738 | ||
742 | static int iser_disconnected_handler(struct rdma_cm_id *cma_id) | 739 | static void iser_disconnected_handler(struct rdma_cm_id *cma_id) |
743 | { | 740 | { |
744 | struct iser_conn *ib_conn; | 741 | struct iser_conn *ib_conn; |
745 | int ret; | ||
746 | 742 | ||
747 | ib_conn = (struct iser_conn *)cma_id->context; | 743 | ib_conn = (struct iser_conn *)cma_id->context; |
748 | 744 | ||
@@ -762,24 +758,19 @@ static int iser_disconnected_handler(struct rdma_cm_id *cma_id) | |||
762 | ib_conn->state = ISER_CONN_DOWN; | 758 | ib_conn->state = ISER_CONN_DOWN; |
763 | wake_up_interruptible(&ib_conn->wait); | 759 | wake_up_interruptible(&ib_conn->wait); |
764 | } | 760 | } |
765 | |||
766 | ret = iser_conn_put(ib_conn, 0); /* deref ib conn's cma id */ | ||
767 | return ret; | ||
768 | } | 761 | } |
769 | 762 | ||
770 | static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) | 763 | static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) |
771 | { | 764 | { |
772 | int ret = 0; | ||
773 | |||
774 | iser_info("event %d status %d conn %p id %p\n", | 765 | iser_info("event %d status %d conn %p id %p\n", |
775 | event->event, event->status, cma_id->context, cma_id); | 766 | event->event, event->status, cma_id->context, cma_id); |
776 | 767 | ||
777 | switch (event->event) { | 768 | switch (event->event) { |
778 | case RDMA_CM_EVENT_ADDR_RESOLVED: | 769 | case RDMA_CM_EVENT_ADDR_RESOLVED: |
779 | ret = iser_addr_handler(cma_id); | 770 | iser_addr_handler(cma_id); |
780 | break; | 771 | break; |
781 | case RDMA_CM_EVENT_ROUTE_RESOLVED: | 772 | case RDMA_CM_EVENT_ROUTE_RESOLVED: |
782 | ret = iser_route_handler(cma_id); | 773 | iser_route_handler(cma_id); |
783 | break; | 774 | break; |
784 | case RDMA_CM_EVENT_ESTABLISHED: | 775 | case RDMA_CM_EVENT_ESTABLISHED: |
785 | iser_connected_handler(cma_id); | 776 | iser_connected_handler(cma_id); |
@@ -789,18 +780,18 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve | |||
789 | case RDMA_CM_EVENT_CONNECT_ERROR: | 780 | case RDMA_CM_EVENT_CONNECT_ERROR: |
790 | case RDMA_CM_EVENT_UNREACHABLE: | 781 | case RDMA_CM_EVENT_UNREACHABLE: |
791 | case RDMA_CM_EVENT_REJECTED: | 782 | case RDMA_CM_EVENT_REJECTED: |
792 | ret = iser_connect_error(cma_id); | 783 | iser_connect_error(cma_id); |
793 | break; | 784 | break; |
794 | case RDMA_CM_EVENT_DISCONNECTED: | 785 | case RDMA_CM_EVENT_DISCONNECTED: |
795 | case RDMA_CM_EVENT_DEVICE_REMOVAL: | 786 | case RDMA_CM_EVENT_DEVICE_REMOVAL: |
796 | case RDMA_CM_EVENT_ADDR_CHANGE: | 787 | case RDMA_CM_EVENT_ADDR_CHANGE: |
797 | ret = iser_disconnected_handler(cma_id); | 788 | iser_disconnected_handler(cma_id); |
798 | break; | 789 | break; |
799 | default: | 790 | default: |
800 | iser_err("Unexpected RDMA CM event (%d)\n", event->event); | 791 | iser_err("Unexpected RDMA CM event (%d)\n", event->event); |
801 | break; | 792 | break; |
802 | } | 793 | } |
803 | return ret; | 794 | return 0; |
804 | } | 795 | } |
805 | 796 | ||
806 | void iser_conn_init(struct iser_conn *ib_conn) | 797 | void iser_conn_init(struct iser_conn *ib_conn) |
@@ -809,7 +800,7 @@ void iser_conn_init(struct iser_conn *ib_conn) | |||
809 | init_waitqueue_head(&ib_conn->wait); | 800 | init_waitqueue_head(&ib_conn->wait); |
810 | ib_conn->post_recv_buf_count = 0; | 801 | ib_conn->post_recv_buf_count = 0; |
811 | atomic_set(&ib_conn->post_send_buf_count, 0); | 802 | atomic_set(&ib_conn->post_send_buf_count, 0); |
812 | atomic_set(&ib_conn->refcount, 1); /* ref ib conn allocation */ | 803 | init_completion(&ib_conn->stop_completion); |
813 | INIT_LIST_HEAD(&ib_conn->conn_list); | 804 | INIT_LIST_HEAD(&ib_conn->conn_list); |
814 | spin_lock_init(&ib_conn->lock); | 805 | spin_lock_init(&ib_conn->lock); |
815 | } | 806 | } |
@@ -837,7 +828,6 @@ int iser_connect(struct iser_conn *ib_conn, | |||
837 | 828 | ||
838 | ib_conn->state = ISER_CONN_PENDING; | 829 | ib_conn->state = ISER_CONN_PENDING; |
839 | 830 | ||
840 | iser_conn_get(ib_conn); /* ref ib conn's cma id */ | ||
841 | ib_conn->cma_id = rdma_create_id(iser_cma_handler, | 831 | ib_conn->cma_id = rdma_create_id(iser_cma_handler, |
842 | (void *)ib_conn, | 832 | (void *)ib_conn, |
843 | RDMA_PS_TCP, IB_QPT_RC); | 833 | RDMA_PS_TCP, IB_QPT_RC); |
@@ -874,9 +864,8 @@ id_failure: | |||
874 | ib_conn->cma_id = NULL; | 864 | ib_conn->cma_id = NULL; |
875 | addr_failure: | 865 | addr_failure: |
876 | ib_conn->state = ISER_CONN_DOWN; | 866 | ib_conn->state = ISER_CONN_DOWN; |
877 | iser_conn_put(ib_conn, 1); /* deref ib conn's cma id */ | ||
878 | connect_failure: | 867 | connect_failure: |
879 | iser_conn_put(ib_conn, 1); /* deref ib conn deallocate */ | 868 | iser_conn_release(ib_conn); |
880 | return err; | 869 | return err; |
881 | } | 870 | } |
882 | 871 | ||