aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorAriel Nahum <arieln@mellanox.com>2014-07-31 06:27:49 -0400
committerRoland Dreier <roland@purestorage.com>2014-08-01 18:10:05 -0400
commit504130c039f917aba8b145fe8ea99be95e662fca (patch)
tree601a9605d25be528f8e60c9e9161d6c30d06181f /drivers/infiniband
parentf1a8bf0983207bebebd13c0507cb341fbffc5ed7 (diff)
IB/iser: Protect iser state machine with a mutex
The iser connection state lookups and transitions are not fully protected. Some transitions are protected with a spinlock, and in some cases the state is accessed unprotected due to specific assumptions of the flow. Introduce a new mutex to protect the connection state access. We use a mutex since we need to also include a scheduling operations executed under the state lock. Each state transition/condition and its corresponding action will be protected with the state mutex. The rdma_cm events handler acquires the mutex when handling connection events. Since iser connection state can transition to DOWN concurrently during connection establishment, we bailout from addr/route resolution events when the state is not PENDING. This addresses a scenario where ep_poll retries expire during CMA connection establishment. In this case ep_disconnect is invoked while CMA events keep coming (address/route resolution, connected, etc...). Signed-off-by: Ariel Nahum <arieln@mellanox.com> Signed-off-by: Sagi Grimberg <sagig@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c15
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h1
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c63
3 files changed, 65 insertions, 14 deletions
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index d7acd4b62d6e..3dc853c9e4bf 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -632,10 +632,13 @@ iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
632 msecs_to_jiffies(timeout_ms)); 632 msecs_to_jiffies(timeout_ms));
633 633
634 /* if conn establishment failed, return error code to iscsi */ 634 /* if conn establishment failed, return error code to iscsi */
635 if (!rc && 635 if (rc == 0) {
636 (ib_conn->state == ISER_CONN_TERMINATING || 636 mutex_lock(&ib_conn->state_mutex);
637 ib_conn->state == ISER_CONN_DOWN)) 637 if (ib_conn->state == ISER_CONN_TERMINATING ||
638 rc = -1; 638 ib_conn->state == ISER_CONN_DOWN)
639 rc = -1;
640 mutex_unlock(&ib_conn->state_mutex);
641 }
639 642
640 iser_info("ib conn %p rc = %d\n", ib_conn, rc); 643 iser_info("ib conn %p rc = %d\n", ib_conn, rc);
641 644
@@ -654,6 +657,7 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
654 657
655 ib_conn = ep->dd_data; 658 ib_conn = ep->dd_data;
656 iser_info("ep %p ib conn %p state %d\n", ep, ib_conn, ib_conn->state); 659 iser_info("ep %p ib conn %p state %d\n", ep, ib_conn, ib_conn->state);
660 mutex_lock(&ib_conn->state_mutex);
657 iser_conn_terminate(ib_conn); 661 iser_conn_terminate(ib_conn);
658 662
659 /* 663 /*
@@ -664,7 +668,10 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
664 if (ib_conn->iscsi_conn) { 668 if (ib_conn->iscsi_conn) {
665 INIT_WORK(&ib_conn->release_work, iser_release_work); 669 INIT_WORK(&ib_conn->release_work, iser_release_work);
666 queue_work(release_wq, &ib_conn->release_work); 670 queue_work(release_wq, &ib_conn->release_work);
671 mutex_unlock(&ib_conn->state_mutex);
667 } else { 672 } else {
673 ib_conn->state = ISER_CONN_DOWN;
674 mutex_unlock(&ib_conn->state_mutex);
668 iser_conn_release(ib_conn); 675 iser_conn_release(ib_conn);
669 } 676 }
670 iscsi_destroy_endpoint(ep); 677 iscsi_destroy_endpoint(ep);
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 37e928477b08..c7efc5a91604 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -335,6 +335,7 @@ struct iser_conn {
335 char name[ISER_OBJECT_NAME_SIZE]; 335 char name[ISER_OBJECT_NAME_SIZE];
336 struct work_struct release_work; 336 struct work_struct release_work;
337 struct completion stop_completion; 337 struct completion stop_completion;
338 struct mutex state_mutex;
338 struct list_head conn_list; /* entry in ig conn list */ 339 struct list_head conn_list; /* entry in ig conn list */
339 340
340 char *login_buf; 341 char *login_buf;
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index a5372c6cf9c6..6e7e54d883ab 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -565,16 +565,17 @@ static void iser_device_try_release(struct iser_device *device)
565 mutex_unlock(&ig.device_list_mutex); 565 mutex_unlock(&ig.device_list_mutex);
566} 566}
567 567
568/**
569 * Called with state mutex held
570 **/
568static int iser_conn_state_comp_exch(struct iser_conn *ib_conn, 571static int iser_conn_state_comp_exch(struct iser_conn *ib_conn,
569 enum iser_ib_conn_state comp, 572 enum iser_ib_conn_state comp,
570 enum iser_ib_conn_state exch) 573 enum iser_ib_conn_state exch)
571{ 574{
572 int ret; 575 int ret;
573 576
574 spin_lock_bh(&ib_conn->lock);
575 if ((ret = (ib_conn->state == comp))) 577 if ((ret = (ib_conn->state == comp)))
576 ib_conn->state = exch; 578 ib_conn->state = exch;
577 spin_unlock_bh(&ib_conn->lock);
578 return ret; 579 return ret;
579} 580}
580 581
@@ -591,6 +592,10 @@ void iser_release_work(struct work_struct *work)
591 wait_event_interruptible(ib_conn->wait, 592 wait_event_interruptible(ib_conn->wait,
592 ib_conn->state == ISER_CONN_DOWN); 593 ib_conn->state == ISER_CONN_DOWN);
593 594
595 mutex_lock(&ib_conn->state_mutex);
596 ib_conn->state = ISER_CONN_DOWN;
597 mutex_unlock(&ib_conn->state_mutex);
598
594 iser_conn_release(ib_conn); 599 iser_conn_release(ib_conn);
595} 600}
596 601
@@ -601,17 +606,21 @@ void iser_conn_release(struct iser_conn *ib_conn)
601{ 606{
602 struct iser_device *device = ib_conn->device; 607 struct iser_device *device = ib_conn->device;
603 608
604 BUG_ON(ib_conn->state == ISER_CONN_UP);
605
606 mutex_lock(&ig.connlist_mutex); 609 mutex_lock(&ig.connlist_mutex);
607 list_del(&ib_conn->conn_list); 610 list_del(&ib_conn->conn_list);
608 mutex_unlock(&ig.connlist_mutex); 611 mutex_unlock(&ig.connlist_mutex);
612
613 mutex_lock(&ib_conn->state_mutex);
614 BUG_ON(ib_conn->state != ISER_CONN_DOWN);
615
609 iser_free_rx_descriptors(ib_conn); 616 iser_free_rx_descriptors(ib_conn);
610 iser_free_ib_conn_res(ib_conn); 617 iser_free_ib_conn_res(ib_conn);
611 ib_conn->device = NULL; 618 ib_conn->device = NULL;
612 /* on EVENT_ADDR_ERROR there's no device yet for this conn */ 619 /* on EVENT_ADDR_ERROR there's no device yet for this conn */
613 if (device != NULL) 620 if (device != NULL)
614 iser_device_try_release(device); 621 iser_device_try_release(device);
622 mutex_unlock(&ib_conn->state_mutex);
623
615 /* if cma handler context, the caller actually destroy the id */ 624 /* if cma handler context, the caller actually destroy the id */
616 if (ib_conn->cma_id != NULL) { 625 if (ib_conn->cma_id != NULL) {
617 rdma_destroy_id(ib_conn->cma_id); 626 rdma_destroy_id(ib_conn->cma_id);
@@ -639,6 +648,9 @@ void iser_conn_terminate(struct iser_conn *ib_conn)
639 ib_conn,err); 648 ib_conn,err);
640} 649}
641 650
651/**
652 * Called with state mutex held
653 **/
642static void iser_connect_error(struct rdma_cm_id *cma_id) 654static void iser_connect_error(struct rdma_cm_id *cma_id)
643{ 655{
644 struct iser_conn *ib_conn; 656 struct iser_conn *ib_conn;
@@ -649,12 +661,20 @@ static void iser_connect_error(struct rdma_cm_id *cma_id)
649 wake_up_interruptible(&ib_conn->wait); 661 wake_up_interruptible(&ib_conn->wait);
650} 662}
651 663
664/**
665 * Called with state mutex held
666 **/
652static void iser_addr_handler(struct rdma_cm_id *cma_id) 667static void iser_addr_handler(struct rdma_cm_id *cma_id)
653{ 668{
654 struct iser_device *device; 669 struct iser_device *device;
655 struct iser_conn *ib_conn; 670 struct iser_conn *ib_conn;
656 int ret; 671 int ret;
657 672
673 ib_conn = (struct iser_conn *)cma_id->context;
674 if (ib_conn->state != ISER_CONN_PENDING)
675 /* bailout */
676 return;
677
658 device = iser_device_find_by_ib_device(cma_id); 678 device = iser_device_find_by_ib_device(cma_id);
659 if (!device) { 679 if (!device) {
660 iser_err("device lookup/creation failed\n"); 680 iser_err("device lookup/creation failed\n");
@@ -662,7 +682,6 @@ static void iser_addr_handler(struct rdma_cm_id *cma_id)
662 return; 682 return;
663 } 683 }
664 684
665 ib_conn = (struct iser_conn *)cma_id->context;
666 ib_conn->device = device; 685 ib_conn->device = device;
667 686
668 /* connection T10-PI support */ 687 /* connection T10-PI support */
@@ -686,6 +705,9 @@ static void iser_addr_handler(struct rdma_cm_id *cma_id)
686 } 705 }
687} 706}
688 707
708/**
709 * Called with state mutex held
710 **/
689static void iser_route_handler(struct rdma_cm_id *cma_id) 711static void iser_route_handler(struct rdma_cm_id *cma_id)
690{ 712{
691 struct rdma_conn_param conn_param; 713 struct rdma_conn_param conn_param;
@@ -694,6 +716,10 @@ static void iser_route_handler(struct rdma_cm_id *cma_id)
694 struct iser_conn *ib_conn = (struct iser_conn *)cma_id->context; 716 struct iser_conn *ib_conn = (struct iser_conn *)cma_id->context;
695 struct iser_device *device = ib_conn->device; 717 struct iser_device *device = ib_conn->device;
696 718
719 if (ib_conn->state != ISER_CONN_PENDING)
720 /* bailout */
721 return;
722
697 ret = iser_create_ib_conn_res((struct iser_conn *)cma_id->context); 723 ret = iser_create_ib_conn_res((struct iser_conn *)cma_id->context);
698 if (ret) 724 if (ret)
699 goto failure; 725 goto failure;
@@ -727,6 +753,11 @@ static void iser_connected_handler(struct rdma_cm_id *cma_id)
727 struct ib_qp_attr attr; 753 struct ib_qp_attr attr;
728 struct ib_qp_init_attr init_attr; 754 struct ib_qp_init_attr init_attr;
729 755
756 ib_conn = (struct iser_conn *)cma_id->context;
757 if (ib_conn->state != ISER_CONN_PENDING)
758 /* bailout */
759 return;
760
730 (void)ib_query_qp(cma_id->qp, &attr, ~0, &init_attr); 761 (void)ib_query_qp(cma_id->qp, &attr, ~0, &init_attr);
731 iser_info("remote qpn:%x my qpn:%x\n", attr.dest_qp_num, cma_id->qp->qp_num); 762 iser_info("remote qpn:%x my qpn:%x\n", attr.dest_qp_num, cma_id->qp->qp_num);
732 763
@@ -761,9 +792,13 @@ static void iser_disconnected_handler(struct rdma_cm_id *cma_id)
761 792
762static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) 793static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
763{ 794{
795 struct iser_conn *ib_conn;
796
797 ib_conn = (struct iser_conn *)cma_id->context;
764 iser_info("event %d status %d conn %p id %p\n", 798 iser_info("event %d status %d conn %p id %p\n",
765 event->event, event->status, cma_id->context, cma_id); 799 event->event, event->status, cma_id->context, cma_id);
766 800
801 mutex_lock(&ib_conn->state_mutex);
767 switch (event->event) { 802 switch (event->event) {
768 case RDMA_CM_EVENT_ADDR_RESOLVED: 803 case RDMA_CM_EVENT_ADDR_RESOLVED:
769 iser_addr_handler(cma_id); 804 iser_addr_handler(cma_id);
@@ -791,6 +826,7 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
791 iser_err("Unexpected RDMA CM event (%d)\n", event->event); 826 iser_err("Unexpected RDMA CM event (%d)\n", event->event);
792 break; 827 break;
793 } 828 }
829 mutex_unlock(&ib_conn->state_mutex);
794 return 0; 830 return 0;
795} 831}
796 832
@@ -803,6 +839,7 @@ void iser_conn_init(struct iser_conn *ib_conn)
803 init_completion(&ib_conn->stop_completion); 839 init_completion(&ib_conn->stop_completion);
804 INIT_LIST_HEAD(&ib_conn->conn_list); 840 INIT_LIST_HEAD(&ib_conn->conn_list);
805 spin_lock_init(&ib_conn->lock); 841 spin_lock_init(&ib_conn->lock);
842 mutex_init(&ib_conn->state_mutex);
806} 843}
807 844
808 /** 845 /**
@@ -816,6 +853,8 @@ int iser_connect(struct iser_conn *ib_conn,
816{ 853{
817 int err = 0; 854 int err = 0;
818 855
856 mutex_lock(&ib_conn->state_mutex);
857
819 sprintf(ib_conn->name, "%pISp", dst_addr); 858 sprintf(ib_conn->name, "%pISp", dst_addr);
820 859
821 iser_info("connecting to: %s\n", ib_conn->name); 860 iser_info("connecting to: %s\n", ib_conn->name);
@@ -849,6 +888,7 @@ int iser_connect(struct iser_conn *ib_conn,
849 goto connect_failure; 888 goto connect_failure;
850 } 889 }
851 } 890 }
891 mutex_unlock(&ib_conn->state_mutex);
852 892
853 mutex_lock(&ig.connlist_mutex); 893 mutex_lock(&ig.connlist_mutex);
854 list_add(&ib_conn->conn_list, &ig.connlist); 894 list_add(&ib_conn->conn_list, &ig.connlist);
@@ -860,6 +900,7 @@ id_failure:
860addr_failure: 900addr_failure:
861 ib_conn->state = ISER_CONN_DOWN; 901 ib_conn->state = ISER_CONN_DOWN;
862connect_failure: 902connect_failure:
903 mutex_unlock(&ib_conn->state_mutex);
863 iser_conn_release(ib_conn); 904 iser_conn_release(ib_conn);
864 return err; 905 return err;
865} 906}
@@ -1044,11 +1085,13 @@ static void iser_handle_comp_error(struct iser_tx_desc *desc,
1044 1085
1045 if (ib_conn->post_recv_buf_count == 0 && 1086 if (ib_conn->post_recv_buf_count == 0 &&
1046 atomic_read(&ib_conn->post_send_buf_count) == 0) { 1087 atomic_read(&ib_conn->post_send_buf_count) == 0) {
1047 /* getting here when the state is UP means that the conn is * 1088 /**
1048 * being terminated asynchronously from the iSCSI layer's * 1089 * getting here when the state is UP means that the conn is
1049 * perspective. */ 1090 * being terminated asynchronously from the iSCSI layer's
1050 if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, 1091 * perspective. It is safe to peek at the connection state
1051 ISER_CONN_TERMINATING)) 1092 * since iscsi_conn_failure is allowed to be called twice.
1093 **/
1094 if (ib_conn->state == ISER_CONN_UP)
1052 iscsi_conn_failure(ib_conn->iscsi_conn, 1095 iscsi_conn_failure(ib_conn->iscsi_conn,
1053 ISCSI_ERR_CONN_FAILED); 1096 ISCSI_ERR_CONN_FAILED);
1054 1097