diff options
author | Ariel Nahum <arieln@mellanox.com> | 2014-07-31 06:27:49 -0400 |
---|---|---|
committer | Roland Dreier <roland@purestorage.com> | 2014-08-01 18:10:05 -0400 |
commit | 504130c039f917aba8b145fe8ea99be95e662fca (patch) | |
tree | 601a9605d25be528f8e60c9e9161d6c30d06181f /drivers/infiniband | |
parent | f1a8bf0983207bebebd13c0507cb341fbffc5ed7 (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.c | 15 | ||||
-rw-r--r-- | drivers/infiniband/ulp/iser/iscsi_iser.h | 1 | ||||
-rw-r--r-- | drivers/infiniband/ulp/iser/iser_verbs.c | 63 |
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 | **/ | ||
568 | static int iser_conn_state_comp_exch(struct iser_conn *ib_conn, | 571 | static 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 | **/ | ||
642 | static void iser_connect_error(struct rdma_cm_id *cma_id) | 654 | static 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 | **/ | ||
652 | static void iser_addr_handler(struct rdma_cm_id *cma_id) | 667 | static 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 | **/ | ||
689 | static void iser_route_handler(struct rdma_cm_id *cma_id) | 711 | static 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 | ||
762 | static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) | 793 | static 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: | |||
860 | addr_failure: | 900 | addr_failure: |
861 | ib_conn->state = ISER_CONN_DOWN; | 901 | ib_conn->state = ISER_CONN_DOWN; |
862 | connect_failure: | 902 | connect_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 | ||