diff options
-rw-r--r-- | drivers/infiniband/core/cm.c | 54 |
1 files changed, 36 insertions, 18 deletions
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index f35fcc4c0638..470c482f2887 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c | |||
@@ -75,6 +75,7 @@ static struct ib_cm { | |||
75 | struct rb_root remote_sidr_table; | 75 | struct rb_root remote_sidr_table; |
76 | struct idr local_id_table; | 76 | struct idr local_id_table; |
77 | __be32 random_id_operand; | 77 | __be32 random_id_operand; |
78 | struct list_head timewait_list; | ||
78 | struct workqueue_struct *wq; | 79 | struct workqueue_struct *wq; |
79 | } cm; | 80 | } cm; |
80 | 81 | ||
@@ -112,6 +113,7 @@ struct cm_work { | |||
112 | 113 | ||
113 | struct cm_timewait_info { | 114 | struct cm_timewait_info { |
114 | struct cm_work work; /* Must be first. */ | 115 | struct cm_work work; /* Must be first. */ |
116 | struct list_head list; | ||
115 | struct rb_node remote_qp_node; | 117 | struct rb_node remote_qp_node; |
116 | struct rb_node remote_id_node; | 118 | struct rb_node remote_id_node; |
117 | __be64 remote_ca_guid; | 119 | __be64 remote_ca_guid; |
@@ -647,13 +649,6 @@ static inline int cm_convert_to_ms(int iba_time) | |||
647 | 649 | ||
648 | static void cm_cleanup_timewait(struct cm_timewait_info *timewait_info) | 650 | static void cm_cleanup_timewait(struct cm_timewait_info *timewait_info) |
649 | { | 651 | { |
650 | unsigned long flags; | ||
651 | |||
652 | if (!timewait_info->inserted_remote_id && | ||
653 | !timewait_info->inserted_remote_qp) | ||
654 | return; | ||
655 | |||
656 | spin_lock_irqsave(&cm.lock, flags); | ||
657 | if (timewait_info->inserted_remote_id) { | 652 | if (timewait_info->inserted_remote_id) { |
658 | rb_erase(&timewait_info->remote_id_node, &cm.remote_id_table); | 653 | rb_erase(&timewait_info->remote_id_node, &cm.remote_id_table); |
659 | timewait_info->inserted_remote_id = 0; | 654 | timewait_info->inserted_remote_id = 0; |
@@ -663,7 +658,6 @@ static void cm_cleanup_timewait(struct cm_timewait_info *timewait_info) | |||
663 | rb_erase(&timewait_info->remote_qp_node, &cm.remote_qp_table); | 658 | rb_erase(&timewait_info->remote_qp_node, &cm.remote_qp_table); |
664 | timewait_info->inserted_remote_qp = 0; | 659 | timewait_info->inserted_remote_qp = 0; |
665 | } | 660 | } |
666 | spin_unlock_irqrestore(&cm.lock, flags); | ||
667 | } | 661 | } |
668 | 662 | ||
669 | static struct cm_timewait_info * cm_create_timewait_info(__be32 local_id) | 663 | static struct cm_timewait_info * cm_create_timewait_info(__be32 local_id) |
@@ -684,8 +678,12 @@ static struct cm_timewait_info * cm_create_timewait_info(__be32 local_id) | |||
684 | static void cm_enter_timewait(struct cm_id_private *cm_id_priv) | 678 | static void cm_enter_timewait(struct cm_id_private *cm_id_priv) |
685 | { | 679 | { |
686 | int wait_time; | 680 | int wait_time; |
681 | unsigned long flags; | ||
687 | 682 | ||
683 | spin_lock_irqsave(&cm.lock, flags); | ||
688 | cm_cleanup_timewait(cm_id_priv->timewait_info); | 684 | cm_cleanup_timewait(cm_id_priv->timewait_info); |
685 | list_add_tail(&cm_id_priv->timewait_info->list, &cm.timewait_list); | ||
686 | spin_unlock_irqrestore(&cm.lock, flags); | ||
689 | 687 | ||
690 | /* | 688 | /* |
691 | * The cm_id could be destroyed by the user before we exit timewait. | 689 | * The cm_id could be destroyed by the user before we exit timewait. |
@@ -701,9 +699,13 @@ static void cm_enter_timewait(struct cm_id_private *cm_id_priv) | |||
701 | 699 | ||
702 | static void cm_reset_to_idle(struct cm_id_private *cm_id_priv) | 700 | static void cm_reset_to_idle(struct cm_id_private *cm_id_priv) |
703 | { | 701 | { |
702 | unsigned long flags; | ||
703 | |||
704 | cm_id_priv->id.state = IB_CM_IDLE; | 704 | cm_id_priv->id.state = IB_CM_IDLE; |
705 | if (cm_id_priv->timewait_info) { | 705 | if (cm_id_priv->timewait_info) { |
706 | spin_lock_irqsave(&cm.lock, flags); | ||
706 | cm_cleanup_timewait(cm_id_priv->timewait_info); | 707 | cm_cleanup_timewait(cm_id_priv->timewait_info); |
708 | spin_unlock_irqrestore(&cm.lock, flags); | ||
707 | kfree(cm_id_priv->timewait_info); | 709 | kfree(cm_id_priv->timewait_info); |
708 | cm_id_priv->timewait_info = NULL; | 710 | cm_id_priv->timewait_info = NULL; |
709 | } | 711 | } |
@@ -1307,6 +1309,7 @@ static struct cm_id_private * cm_match_req(struct cm_work *work, | |||
1307 | if (timewait_info) { | 1309 | if (timewait_info) { |
1308 | cur_cm_id_priv = cm_get_id(timewait_info->work.local_id, | 1310 | cur_cm_id_priv = cm_get_id(timewait_info->work.local_id, |
1309 | timewait_info->work.remote_id); | 1311 | timewait_info->work.remote_id); |
1312 | cm_cleanup_timewait(cm_id_priv->timewait_info); | ||
1310 | spin_unlock_irqrestore(&cm.lock, flags); | 1313 | spin_unlock_irqrestore(&cm.lock, flags); |
1311 | if (cur_cm_id_priv) { | 1314 | if (cur_cm_id_priv) { |
1312 | cm_dup_req_handler(work, cur_cm_id_priv); | 1315 | cm_dup_req_handler(work, cur_cm_id_priv); |
@@ -1315,7 +1318,8 @@ static struct cm_id_private * cm_match_req(struct cm_work *work, | |||
1315 | cm_issue_rej(work->port, work->mad_recv_wc, | 1318 | cm_issue_rej(work->port, work->mad_recv_wc, |
1316 | IB_CM_REJ_STALE_CONN, CM_MSG_RESPONSE_REQ, | 1319 | IB_CM_REJ_STALE_CONN, CM_MSG_RESPONSE_REQ, |
1317 | NULL, 0); | 1320 | NULL, 0); |
1318 | goto error; | 1321 | listen_cm_id_priv = NULL; |
1322 | goto out; | ||
1319 | } | 1323 | } |
1320 | 1324 | ||
1321 | /* Find matching listen request. */ | 1325 | /* Find matching listen request. */ |
@@ -1323,21 +1327,20 @@ static struct cm_id_private * cm_match_req(struct cm_work *work, | |||
1323 | req_msg->service_id, | 1327 | req_msg->service_id, |
1324 | req_msg->private_data); | 1328 | req_msg->private_data); |
1325 | if (!listen_cm_id_priv) { | 1329 | if (!listen_cm_id_priv) { |
1330 | cm_cleanup_timewait(cm_id_priv->timewait_info); | ||
1326 | spin_unlock_irqrestore(&cm.lock, flags); | 1331 | spin_unlock_irqrestore(&cm.lock, flags); |
1327 | cm_issue_rej(work->port, work->mad_recv_wc, | 1332 | cm_issue_rej(work->port, work->mad_recv_wc, |
1328 | IB_CM_REJ_INVALID_SERVICE_ID, CM_MSG_RESPONSE_REQ, | 1333 | IB_CM_REJ_INVALID_SERVICE_ID, CM_MSG_RESPONSE_REQ, |
1329 | NULL, 0); | 1334 | NULL, 0); |
1330 | goto error; | 1335 | goto out; |
1331 | } | 1336 | } |
1332 | atomic_inc(&listen_cm_id_priv->refcount); | 1337 | atomic_inc(&listen_cm_id_priv->refcount); |
1333 | atomic_inc(&cm_id_priv->refcount); | 1338 | atomic_inc(&cm_id_priv->refcount); |
1334 | cm_id_priv->id.state = IB_CM_REQ_RCVD; | 1339 | cm_id_priv->id.state = IB_CM_REQ_RCVD; |
1335 | atomic_inc(&cm_id_priv->work_count); | 1340 | atomic_inc(&cm_id_priv->work_count); |
1336 | spin_unlock_irqrestore(&cm.lock, flags); | 1341 | spin_unlock_irqrestore(&cm.lock, flags); |
1342 | out: | ||
1337 | return listen_cm_id_priv; | 1343 | return listen_cm_id_priv; |
1338 | |||
1339 | error: cm_cleanup_timewait(cm_id_priv->timewait_info); | ||
1340 | return NULL; | ||
1341 | } | 1344 | } |
1342 | 1345 | ||
1343 | static int cm_req_handler(struct cm_work *work) | 1346 | static int cm_req_handler(struct cm_work *work) |
@@ -2601,28 +2604,29 @@ static int cm_timewait_handler(struct cm_work *work) | |||
2601 | { | 2604 | { |
2602 | struct cm_timewait_info *timewait_info; | 2605 | struct cm_timewait_info *timewait_info; |
2603 | struct cm_id_private *cm_id_priv; | 2606 | struct cm_id_private *cm_id_priv; |
2604 | unsigned long flags; | ||
2605 | int ret; | 2607 | int ret; |
2606 | 2608 | ||
2607 | timewait_info = (struct cm_timewait_info *)work; | 2609 | timewait_info = (struct cm_timewait_info *)work; |
2608 | cm_cleanup_timewait(timewait_info); | 2610 | spin_lock_irq(&cm.lock); |
2611 | list_del(&timewait_info->list); | ||
2612 | spin_unlock_irq(&cm.lock); | ||
2609 | 2613 | ||
2610 | cm_id_priv = cm_acquire_id(timewait_info->work.local_id, | 2614 | cm_id_priv = cm_acquire_id(timewait_info->work.local_id, |
2611 | timewait_info->work.remote_id); | 2615 | timewait_info->work.remote_id); |
2612 | if (!cm_id_priv) | 2616 | if (!cm_id_priv) |
2613 | return -EINVAL; | 2617 | return -EINVAL; |
2614 | 2618 | ||
2615 | spin_lock_irqsave(&cm_id_priv->lock, flags); | 2619 | spin_lock_irq(&cm_id_priv->lock); |
2616 | if (cm_id_priv->id.state != IB_CM_TIMEWAIT || | 2620 | if (cm_id_priv->id.state != IB_CM_TIMEWAIT || |
2617 | cm_id_priv->remote_qpn != timewait_info->remote_qpn) { | 2621 | cm_id_priv->remote_qpn != timewait_info->remote_qpn) { |
2618 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | 2622 | spin_unlock_irq(&cm_id_priv->lock); |
2619 | goto out; | 2623 | goto out; |
2620 | } | 2624 | } |
2621 | cm_id_priv->id.state = IB_CM_IDLE; | 2625 | cm_id_priv->id.state = IB_CM_IDLE; |
2622 | ret = atomic_inc_and_test(&cm_id_priv->work_count); | 2626 | ret = atomic_inc_and_test(&cm_id_priv->work_count); |
2623 | if (!ret) | 2627 | if (!ret) |
2624 | list_add_tail(&work->list, &cm_id_priv->work_list); | 2628 | list_add_tail(&work->list, &cm_id_priv->work_list); |
2625 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | 2629 | spin_unlock_irq(&cm_id_priv->lock); |
2626 | 2630 | ||
2627 | if (ret) | 2631 | if (ret) |
2628 | cm_process_work(cm_id_priv, work); | 2632 | cm_process_work(cm_id_priv, work); |
@@ -3374,6 +3378,7 @@ static int __init ib_cm_init(void) | |||
3374 | idr_init(&cm.local_id_table); | 3378 | idr_init(&cm.local_id_table); |
3375 | get_random_bytes(&cm.random_id_operand, sizeof cm.random_id_operand); | 3379 | get_random_bytes(&cm.random_id_operand, sizeof cm.random_id_operand); |
3376 | idr_pre_get(&cm.local_id_table, GFP_KERNEL); | 3380 | idr_pre_get(&cm.local_id_table, GFP_KERNEL); |
3381 | INIT_LIST_HEAD(&cm.timewait_list); | ||
3377 | 3382 | ||
3378 | cm.wq = create_workqueue("ib_cm"); | 3383 | cm.wq = create_workqueue("ib_cm"); |
3379 | if (!cm.wq) | 3384 | if (!cm.wq) |
@@ -3391,7 +3396,20 @@ error: | |||
3391 | 3396 | ||
3392 | static void __exit ib_cm_cleanup(void) | 3397 | static void __exit ib_cm_cleanup(void) |
3393 | { | 3398 | { |
3399 | struct cm_timewait_info *timewait_info, *tmp; | ||
3400 | |||
3401 | spin_lock_irq(&cm.lock); | ||
3402 | list_for_each_entry(timewait_info, &cm.timewait_list, list) | ||
3403 | cancel_delayed_work(&timewait_info->work.work); | ||
3404 | spin_unlock_irq(&cm.lock); | ||
3405 | |||
3394 | destroy_workqueue(cm.wq); | 3406 | destroy_workqueue(cm.wq); |
3407 | |||
3408 | list_for_each_entry_safe(timewait_info, tmp, &cm.timewait_list, list) { | ||
3409 | list_del(&timewait_info->list); | ||
3410 | kfree(timewait_info); | ||
3411 | } | ||
3412 | |||
3395 | ib_unregister_client(&cm_client); | 3413 | ib_unregister_client(&cm_client); |
3396 | idr_destroy(&cm.local_id_table); | 3414 | idr_destroy(&cm.local_id_table); |
3397 | } | 3415 | } |