aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/ulp/iser/iser_verbs.c
diff options
context:
space:
mode:
authorAriel Nahum <arieln@mellanox.com>2014-05-22 04:00:18 -0400
committerRoland Dreier <roland@purestorage.com>2014-05-26 11:19:48 -0400
commitb73c3adabdb1e2cb2f2c69bc3cbb9306aa3f9700 (patch)
tree4450f306e659387ffc2ec291a611c89ded989b06 /drivers/infiniband/ulp/iser/iser_verbs.c
parentd6d211db37e75de2ddc3a4f979038c40df7cc79c (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.c85
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
584void 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 */
587static void iser_conn_release(struct iser_conn *ib_conn, int can_destroy_id) 603void 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
610void iser_conn_get(struct iser_conn *ib_conn)
611{
612 atomic_inc(&ib_conn->refcount);
613}
614
615int 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
648static int iser_connect_error(struct rdma_cm_id *cma_id) 645static 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
658static int iser_addr_handler(struct rdma_cm_id *cma_id) 655static 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
695static int iser_route_handler(struct rdma_cm_id *cma_id) 692static 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;
724failure: 721failure:
725 return iser_connect_error(cma_id); 722 iser_connect_error(cma_id);
726} 723}
727 724
728static void iser_connected_handler(struct rdma_cm_id *cma_id) 725static 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
742static int iser_disconnected_handler(struct rdma_cm_id *cma_id) 739static 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
770static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) 763static 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
806void iser_conn_init(struct iser_conn *ib_conn) 797void 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;
875addr_failure: 865addr_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 */
878connect_failure: 867connect_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