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 | |
| 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>
| -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 | ||
