diff options
Diffstat (limited to 'drivers/infiniband/core/cm.c')
-rw-r--r-- | drivers/infiniband/core/cm.c | 119 |
1 files changed, 88 insertions, 31 deletions
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 86fee43502cd..450adfe0a4f1 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c | |||
@@ -32,7 +32,7 @@ | |||
32 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 32 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
33 | * SOFTWARE. | 33 | * SOFTWARE. |
34 | * | 34 | * |
35 | * $Id: cm.c 2821 2005-07-08 17:07:28Z sean.hefty $ | 35 | * $Id: cm.c 4311 2005-12-05 18:42:01Z sean.hefty $ |
36 | */ | 36 | */ |
37 | 37 | ||
38 | #include <linux/completion.h> | 38 | #include <linux/completion.h> |
@@ -132,6 +132,7 @@ struct cm_id_private { | |||
132 | /* todo: use alternate port on send failure */ | 132 | /* todo: use alternate port on send failure */ |
133 | struct cm_av av; | 133 | struct cm_av av; |
134 | struct cm_av alt_av; | 134 | struct cm_av alt_av; |
135 | struct ib_cm_compare_data *compare_data; | ||
135 | 136 | ||
136 | void *private_data; | 137 | void *private_data; |
137 | __be64 tid; | 138 | __be64 tid; |
@@ -253,23 +254,13 @@ static void cm_set_private_data(struct cm_id_private *cm_id_priv, | |||
253 | cm_id_priv->private_data_len = private_data_len; | 254 | cm_id_priv->private_data_len = private_data_len; |
254 | } | 255 | } |
255 | 256 | ||
256 | static void cm_set_ah_attr(struct ib_ah_attr *ah_attr, u8 port_num, | 257 | static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc, |
257 | u16 dlid, u8 sl, u16 src_path_bits) | 258 | struct ib_grh *grh, struct cm_av *av) |
258 | { | ||
259 | memset(ah_attr, 0, sizeof ah_attr); | ||
260 | ah_attr->dlid = dlid; | ||
261 | ah_attr->sl = sl; | ||
262 | ah_attr->src_path_bits = src_path_bits; | ||
263 | ah_attr->port_num = port_num; | ||
264 | } | ||
265 | |||
266 | static void cm_init_av_for_response(struct cm_port *port, | ||
267 | struct ib_wc *wc, struct cm_av *av) | ||
268 | { | 259 | { |
269 | av->port = port; | 260 | av->port = port; |
270 | av->pkey_index = wc->pkey_index; | 261 | av->pkey_index = wc->pkey_index; |
271 | cm_set_ah_attr(&av->ah_attr, port->port_num, wc->slid, | 262 | ib_init_ah_from_wc(port->cm_dev->device, port->port_num, wc, |
272 | wc->sl, wc->dlid_path_bits); | 263 | grh, &av->ah_attr); |
273 | } | 264 | } |
274 | 265 | ||
275 | static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) | 266 | static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) |
@@ -299,9 +290,8 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) | |||
299 | return ret; | 290 | return ret; |
300 | 291 | ||
301 | av->port = port; | 292 | av->port = port; |
302 | cm_set_ah_attr(&av->ah_attr, av->port->port_num, | 293 | ib_init_ah_from_path(cm_dev->device, port->port_num, path, |
303 | be16_to_cpu(path->dlid), path->sl, | 294 | &av->ah_attr); |
304 | be16_to_cpu(path->slid) & 0x7F); | ||
305 | av->packet_life_time = path->packet_life_time; | 295 | av->packet_life_time = path->packet_life_time; |
306 | return 0; | 296 | return 0; |
307 | } | 297 | } |
@@ -357,6 +347,41 @@ static struct cm_id_private * cm_acquire_id(__be32 local_id, __be32 remote_id) | |||
357 | return cm_id_priv; | 347 | return cm_id_priv; |
358 | } | 348 | } |
359 | 349 | ||
350 | static void cm_mask_copy(u8 *dst, u8 *src, u8 *mask) | ||
351 | { | ||
352 | int i; | ||
353 | |||
354 | for (i = 0; i < IB_CM_COMPARE_SIZE / sizeof(unsigned long); i++) | ||
355 | ((unsigned long *) dst)[i] = ((unsigned long *) src)[i] & | ||
356 | ((unsigned long *) mask)[i]; | ||
357 | } | ||
358 | |||
359 | static int cm_compare_data(struct ib_cm_compare_data *src_data, | ||
360 | struct ib_cm_compare_data *dst_data) | ||
361 | { | ||
362 | u8 src[IB_CM_COMPARE_SIZE]; | ||
363 | u8 dst[IB_CM_COMPARE_SIZE]; | ||
364 | |||
365 | if (!src_data || !dst_data) | ||
366 | return 0; | ||
367 | |||
368 | cm_mask_copy(src, src_data->data, dst_data->mask); | ||
369 | cm_mask_copy(dst, dst_data->data, src_data->mask); | ||
370 | return memcmp(src, dst, IB_CM_COMPARE_SIZE); | ||
371 | } | ||
372 | |||
373 | static int cm_compare_private_data(u8 *private_data, | ||
374 | struct ib_cm_compare_data *dst_data) | ||
375 | { | ||
376 | u8 src[IB_CM_COMPARE_SIZE]; | ||
377 | |||
378 | if (!dst_data) | ||
379 | return 0; | ||
380 | |||
381 | cm_mask_copy(src, private_data, dst_data->mask); | ||
382 | return memcmp(src, dst_data->data, IB_CM_COMPARE_SIZE); | ||
383 | } | ||
384 | |||
360 | static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv) | 385 | static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv) |
361 | { | 386 | { |
362 | struct rb_node **link = &cm.listen_service_table.rb_node; | 387 | struct rb_node **link = &cm.listen_service_table.rb_node; |
@@ -364,14 +389,18 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv) | |||
364 | struct cm_id_private *cur_cm_id_priv; | 389 | struct cm_id_private *cur_cm_id_priv; |
365 | __be64 service_id = cm_id_priv->id.service_id; | 390 | __be64 service_id = cm_id_priv->id.service_id; |
366 | __be64 service_mask = cm_id_priv->id.service_mask; | 391 | __be64 service_mask = cm_id_priv->id.service_mask; |
392 | int data_cmp; | ||
367 | 393 | ||
368 | while (*link) { | 394 | while (*link) { |
369 | parent = *link; | 395 | parent = *link; |
370 | cur_cm_id_priv = rb_entry(parent, struct cm_id_private, | 396 | cur_cm_id_priv = rb_entry(parent, struct cm_id_private, |
371 | service_node); | 397 | service_node); |
398 | data_cmp = cm_compare_data(cm_id_priv->compare_data, | ||
399 | cur_cm_id_priv->compare_data); | ||
372 | if ((cur_cm_id_priv->id.service_mask & service_id) == | 400 | if ((cur_cm_id_priv->id.service_mask & service_id) == |
373 | (service_mask & cur_cm_id_priv->id.service_id) && | 401 | (service_mask & cur_cm_id_priv->id.service_id) && |
374 | (cm_id_priv->id.device == cur_cm_id_priv->id.device)) | 402 | (cm_id_priv->id.device == cur_cm_id_priv->id.device) && |
403 | !data_cmp) | ||
375 | return cur_cm_id_priv; | 404 | return cur_cm_id_priv; |
376 | 405 | ||
377 | if (cm_id_priv->id.device < cur_cm_id_priv->id.device) | 406 | if (cm_id_priv->id.device < cur_cm_id_priv->id.device) |
@@ -380,6 +409,10 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv) | |||
380 | link = &(*link)->rb_right; | 409 | link = &(*link)->rb_right; |
381 | else if (service_id < cur_cm_id_priv->id.service_id) | 410 | else if (service_id < cur_cm_id_priv->id.service_id) |
382 | link = &(*link)->rb_left; | 411 | link = &(*link)->rb_left; |
412 | else if (service_id > cur_cm_id_priv->id.service_id) | ||
413 | link = &(*link)->rb_right; | ||
414 | else if (data_cmp < 0) | ||
415 | link = &(*link)->rb_left; | ||
383 | else | 416 | else |
384 | link = &(*link)->rb_right; | 417 | link = &(*link)->rb_right; |
385 | } | 418 | } |
@@ -389,16 +422,20 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv) | |||
389 | } | 422 | } |
390 | 423 | ||
391 | static struct cm_id_private * cm_find_listen(struct ib_device *device, | 424 | static struct cm_id_private * cm_find_listen(struct ib_device *device, |
392 | __be64 service_id) | 425 | __be64 service_id, |
426 | u8 *private_data) | ||
393 | { | 427 | { |
394 | struct rb_node *node = cm.listen_service_table.rb_node; | 428 | struct rb_node *node = cm.listen_service_table.rb_node; |
395 | struct cm_id_private *cm_id_priv; | 429 | struct cm_id_private *cm_id_priv; |
430 | int data_cmp; | ||
396 | 431 | ||
397 | while (node) { | 432 | while (node) { |
398 | cm_id_priv = rb_entry(node, struct cm_id_private, service_node); | 433 | cm_id_priv = rb_entry(node, struct cm_id_private, service_node); |
434 | data_cmp = cm_compare_private_data(private_data, | ||
435 | cm_id_priv->compare_data); | ||
399 | if ((cm_id_priv->id.service_mask & service_id) == | 436 | if ((cm_id_priv->id.service_mask & service_id) == |
400 | cm_id_priv->id.service_id && | 437 | cm_id_priv->id.service_id && |
401 | (cm_id_priv->id.device == device)) | 438 | (cm_id_priv->id.device == device) && !data_cmp) |
402 | return cm_id_priv; | 439 | return cm_id_priv; |
403 | 440 | ||
404 | if (device < cm_id_priv->id.device) | 441 | if (device < cm_id_priv->id.device) |
@@ -407,6 +444,10 @@ static struct cm_id_private * cm_find_listen(struct ib_device *device, | |||
407 | node = node->rb_right; | 444 | node = node->rb_right; |
408 | else if (service_id < cm_id_priv->id.service_id) | 445 | else if (service_id < cm_id_priv->id.service_id) |
409 | node = node->rb_left; | 446 | node = node->rb_left; |
447 | else if (service_id > cm_id_priv->id.service_id) | ||
448 | node = node->rb_right; | ||
449 | else if (data_cmp < 0) | ||
450 | node = node->rb_left; | ||
410 | else | 451 | else |
411 | node = node->rb_right; | 452 | node = node->rb_right; |
412 | } | 453 | } |
@@ -730,15 +771,14 @@ retest: | |||
730 | wait_for_completion(&cm_id_priv->comp); | 771 | wait_for_completion(&cm_id_priv->comp); |
731 | while ((work = cm_dequeue_work(cm_id_priv)) != NULL) | 772 | while ((work = cm_dequeue_work(cm_id_priv)) != NULL) |
732 | cm_free_work(work); | 773 | cm_free_work(work); |
733 | if (cm_id_priv->private_data && cm_id_priv->private_data_len) | 774 | kfree(cm_id_priv->compare_data); |
734 | kfree(cm_id_priv->private_data); | 775 | kfree(cm_id_priv->private_data); |
735 | kfree(cm_id_priv); | 776 | kfree(cm_id_priv); |
736 | } | 777 | } |
737 | EXPORT_SYMBOL(ib_destroy_cm_id); | 778 | EXPORT_SYMBOL(ib_destroy_cm_id); |
738 | 779 | ||
739 | int ib_cm_listen(struct ib_cm_id *cm_id, | 780 | int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask, |
740 | __be64 service_id, | 781 | struct ib_cm_compare_data *compare_data) |
741 | __be64 service_mask) | ||
742 | { | 782 | { |
743 | struct cm_id_private *cm_id_priv, *cur_cm_id_priv; | 783 | struct cm_id_private *cm_id_priv, *cur_cm_id_priv; |
744 | unsigned long flags; | 784 | unsigned long flags; |
@@ -752,7 +792,19 @@ int ib_cm_listen(struct ib_cm_id *cm_id, | |||
752 | return -EINVAL; | 792 | return -EINVAL; |
753 | 793 | ||
754 | cm_id_priv = container_of(cm_id, struct cm_id_private, id); | 794 | cm_id_priv = container_of(cm_id, struct cm_id_private, id); |
755 | BUG_ON(cm_id->state != IB_CM_IDLE); | 795 | if (cm_id->state != IB_CM_IDLE) |
796 | return -EINVAL; | ||
797 | |||
798 | if (compare_data) { | ||
799 | cm_id_priv->compare_data = kzalloc(sizeof *compare_data, | ||
800 | GFP_KERNEL); | ||
801 | if (!cm_id_priv->compare_data) | ||
802 | return -ENOMEM; | ||
803 | cm_mask_copy(cm_id_priv->compare_data->data, | ||
804 | compare_data->data, compare_data->mask); | ||
805 | memcpy(cm_id_priv->compare_data->mask, compare_data->mask, | ||
806 | IB_CM_COMPARE_SIZE); | ||
807 | } | ||
756 | 808 | ||
757 | cm_id->state = IB_CM_LISTEN; | 809 | cm_id->state = IB_CM_LISTEN; |
758 | 810 | ||
@@ -769,6 +821,8 @@ int ib_cm_listen(struct ib_cm_id *cm_id, | |||
769 | 821 | ||
770 | if (cur_cm_id_priv) { | 822 | if (cur_cm_id_priv) { |
771 | cm_id->state = IB_CM_IDLE; | 823 | cm_id->state = IB_CM_IDLE; |
824 | kfree(cm_id_priv->compare_data); | ||
825 | cm_id_priv->compare_data = NULL; | ||
772 | ret = -EBUSY; | 826 | ret = -EBUSY; |
773 | } | 827 | } |
774 | return ret; | 828 | return ret; |
@@ -1241,7 +1295,8 @@ static struct cm_id_private * cm_match_req(struct cm_work *work, | |||
1241 | 1295 | ||
1242 | /* Find matching listen request. */ | 1296 | /* Find matching listen request. */ |
1243 | listen_cm_id_priv = cm_find_listen(cm_id_priv->id.device, | 1297 | listen_cm_id_priv = cm_find_listen(cm_id_priv->id.device, |
1244 | req_msg->service_id); | 1298 | req_msg->service_id, |
1299 | req_msg->private_data); | ||
1245 | if (!listen_cm_id_priv) { | 1300 | if (!listen_cm_id_priv) { |
1246 | spin_unlock_irqrestore(&cm.lock, flags); | 1301 | spin_unlock_irqrestore(&cm.lock, flags); |
1247 | cm_issue_rej(work->port, work->mad_recv_wc, | 1302 | cm_issue_rej(work->port, work->mad_recv_wc, |
@@ -1276,6 +1331,7 @@ static int cm_req_handler(struct cm_work *work) | |||
1276 | cm_id_priv = container_of(cm_id, struct cm_id_private, id); | 1331 | cm_id_priv = container_of(cm_id, struct cm_id_private, id); |
1277 | cm_id_priv->id.remote_id = req_msg->local_comm_id; | 1332 | cm_id_priv->id.remote_id = req_msg->local_comm_id; |
1278 | cm_init_av_for_response(work->port, work->mad_recv_wc->wc, | 1333 | cm_init_av_for_response(work->port, work->mad_recv_wc->wc, |
1334 | work->mad_recv_wc->recv_buf.grh, | ||
1279 | &cm_id_priv->av); | 1335 | &cm_id_priv->av); |
1280 | cm_id_priv->timewait_info = cm_create_timewait_info(cm_id_priv-> | 1336 | cm_id_priv->timewait_info = cm_create_timewait_info(cm_id_priv-> |
1281 | id.local_id); | 1337 | id.local_id); |
@@ -2549,7 +2605,7 @@ static void cm_format_sidr_req(struct cm_sidr_req_msg *sidr_req_msg, | |||
2549 | cm_format_mad_hdr(&sidr_req_msg->hdr, CM_SIDR_REQ_ATTR_ID, | 2605 | cm_format_mad_hdr(&sidr_req_msg->hdr, CM_SIDR_REQ_ATTR_ID, |
2550 | cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_SIDR)); | 2606 | cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_SIDR)); |
2551 | sidr_req_msg->request_id = cm_id_priv->id.local_id; | 2607 | sidr_req_msg->request_id = cm_id_priv->id.local_id; |
2552 | sidr_req_msg->pkey = cpu_to_be16(param->pkey); | 2608 | sidr_req_msg->pkey = cpu_to_be16(param->path->pkey); |
2553 | sidr_req_msg->service_id = param->service_id; | 2609 | sidr_req_msg->service_id = param->service_id; |
2554 | 2610 | ||
2555 | if (param->private_data && param->private_data_len) | 2611 | if (param->private_data && param->private_data_len) |
@@ -2641,6 +2697,7 @@ static int cm_sidr_req_handler(struct cm_work *work) | |||
2641 | cm_id_priv->av.dgid.global.subnet_prefix = cpu_to_be64(wc->slid); | 2697 | cm_id_priv->av.dgid.global.subnet_prefix = cpu_to_be64(wc->slid); |
2642 | cm_id_priv->av.dgid.global.interface_id = 0; | 2698 | cm_id_priv->av.dgid.global.interface_id = 0; |
2643 | cm_init_av_for_response(work->port, work->mad_recv_wc->wc, | 2699 | cm_init_av_for_response(work->port, work->mad_recv_wc->wc, |
2700 | work->mad_recv_wc->recv_buf.grh, | ||
2644 | &cm_id_priv->av); | 2701 | &cm_id_priv->av); |
2645 | cm_id_priv->id.remote_id = sidr_req_msg->request_id; | 2702 | cm_id_priv->id.remote_id = sidr_req_msg->request_id; |
2646 | cm_id_priv->id.state = IB_CM_SIDR_REQ_RCVD; | 2703 | cm_id_priv->id.state = IB_CM_SIDR_REQ_RCVD; |
@@ -2654,7 +2711,8 @@ static int cm_sidr_req_handler(struct cm_work *work) | |||
2654 | goto out; /* Duplicate message. */ | 2711 | goto out; /* Duplicate message. */ |
2655 | } | 2712 | } |
2656 | cur_cm_id_priv = cm_find_listen(cm_id->device, | 2713 | cur_cm_id_priv = cm_find_listen(cm_id->device, |
2657 | sidr_req_msg->service_id); | 2714 | sidr_req_msg->service_id, |
2715 | sidr_req_msg->private_data); | ||
2658 | if (!cur_cm_id_priv) { | 2716 | if (!cur_cm_id_priv) { |
2659 | rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); | 2717 | rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); |
2660 | spin_unlock_irqrestore(&cm.lock, flags); | 2718 | spin_unlock_irqrestore(&cm.lock, flags); |
@@ -3291,7 +3349,6 @@ error: | |||
3291 | 3349 | ||
3292 | static void __exit ib_cm_cleanup(void) | 3350 | static void __exit ib_cm_cleanup(void) |
3293 | { | 3351 | { |
3294 | flush_workqueue(cm.wq); | ||
3295 | destroy_workqueue(cm.wq); | 3352 | destroy_workqueue(cm.wq); |
3296 | ib_unregister_client(&cm_client); | 3353 | ib_unregister_client(&cm_client); |
3297 | idr_destroy(&cm.local_id_table); | 3354 | idr_destroy(&cm.local_id_table); |