aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Hefty <sean.hefty@intel.com>2006-10-04 14:29:59 -0400
committerRoland Dreier <rolandd@cisco.com>2006-10-10 15:50:38 -0400
commit8575329d4f8596519d86830f622d2c30601f3ef3 (patch)
tree6fbcb5200d4bb863018fae5a07bfff9e16b9a976
parenta8bf4e7717142b0688a726dd07501a6a7783792a (diff)
IB/cm: Fix timewait crash after module unload
If the ib_cm module is unloaded while id's are still in timewait, the CM will destroy the work queue used to process timewait. Once the id's exit timewait, their timers will fire, leading to a crash trying to access the destroyed work queue. We need to track id's that are in timewait, and cancel their deferred work on module unload. Signed-off-by: Sean Hefty <sean.hefty@intel.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r--drivers/infiniband/core/cm.c54
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
113struct cm_timewait_info { 114struct 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
648static void cm_cleanup_timewait(struct cm_timewait_info *timewait_info) 650static 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
669static struct cm_timewait_info * cm_create_timewait_info(__be32 local_id) 663static 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)
684static void cm_enter_timewait(struct cm_id_private *cm_id_priv) 678static 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
702static void cm_reset_to_idle(struct cm_id_private *cm_id_priv) 700static 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);
1342out:
1337 return listen_cm_id_priv; 1343 return listen_cm_id_priv;
1338
1339error: cm_cleanup_timewait(cm_id_priv->timewait_info);
1340 return NULL;
1341} 1344}
1342 1345
1343static int cm_req_handler(struct cm_work *work) 1346static 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
3392static void __exit ib_cm_cleanup(void) 3397static 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}