diff options
| author | Sean Hefty <sean.hefty@intel.com> | 2007-02-15 20:00:18 -0500 |
|---|---|---|
| committer | Roland Dreier <rolandd@cisco.com> | 2007-02-16 17:29:07 -0500 |
| commit | c8f6a362bf3eb28ade6027b49bb160a336dd51c0 (patch) | |
| tree | b9b19599c9fddea313725a359283bc624a86c1ec | |
| parent | faec2f7b96b555055d0aa6cc6b83a537270bed52 (diff) | |
RDMA/cma: Add multicast communication support
Extend rdma_cm to support multicast communication. Multicast support
is added to the existing RDMA_PS_UDP port space, as well as a new
RDMA_PS_IPOIB port space. The latter port space allows joining the
multicast groups used by IPoIB, which enables offloading IPoIB traffic
to a separate QP. The port space determines the signature used in the
MGID when joining the group. The newly added RDMA_PS_IPOIB also
allows for unicast operations, similar to RDMA_PS_UDP.
Supporting the RDMA_PS_IPOIB requires changing how UD QPs are initialized,
since we can no longer assume that the qkey is constant. This requires
saving the Q_Key to use when attaching to a device, so that it is
available when creating the QP. The Q_Key information is exported to
the user through the existing rdma_init_qp_attr() interface.
Multicast support is also exported to userspace through the rdma_ucm.
Signed-off-by: Roland Dreier <rolandd@cisco.com>
| -rw-r--r-- | drivers/infiniband/core/cma.c | 359 | ||||
| -rw-r--r-- | drivers/infiniband/core/ucma.c | 204 | ||||
| -rw-r--r-- | include/rdma/rdma_cm.h | 21 | ||||
| -rw-r--r-- | include/rdma/rdma_cm_ib.h | 4 | ||||
| -rw-r--r-- | include/rdma/rdma_user_cm.h | 13 |
5 files changed, 549 insertions, 52 deletions
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index db88e609bf42..f8d69b3fa307 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c | |||
| @@ -71,6 +71,7 @@ static struct workqueue_struct *cma_wq; | |||
| 71 | static DEFINE_IDR(sdp_ps); | 71 | static DEFINE_IDR(sdp_ps); |
| 72 | static DEFINE_IDR(tcp_ps); | 72 | static DEFINE_IDR(tcp_ps); |
| 73 | static DEFINE_IDR(udp_ps); | 73 | static DEFINE_IDR(udp_ps); |
| 74 | static DEFINE_IDR(ipoib_ps); | ||
| 74 | static int next_port; | 75 | static int next_port; |
| 75 | 76 | ||
| 76 | struct cma_device { | 77 | struct cma_device { |
| @@ -116,6 +117,7 @@ struct rdma_id_private { | |||
| 116 | struct list_head list; | 117 | struct list_head list; |
| 117 | struct list_head listen_list; | 118 | struct list_head listen_list; |
| 118 | struct cma_device *cma_dev; | 119 | struct cma_device *cma_dev; |
| 120 | struct list_head mc_list; | ||
| 119 | 121 | ||
| 120 | enum cma_state state; | 122 | enum cma_state state; |
| 121 | spinlock_t lock; | 123 | spinlock_t lock; |
| @@ -134,10 +136,23 @@ struct rdma_id_private { | |||
| 134 | } cm_id; | 136 | } cm_id; |
| 135 | 137 | ||
| 136 | u32 seq_num; | 138 | u32 seq_num; |
| 139 | u32 qkey; | ||
| 137 | u32 qp_num; | 140 | u32 qp_num; |
| 138 | u8 srq; | 141 | u8 srq; |
| 139 | }; | 142 | }; |
| 140 | 143 | ||
| 144 | struct cma_multicast { | ||
| 145 | struct rdma_id_private *id_priv; | ||
| 146 | union { | ||
| 147 | struct ib_sa_multicast *ib; | ||
| 148 | } multicast; | ||
| 149 | struct list_head list; | ||
| 150 | void *context; | ||
| 151 | struct sockaddr addr; | ||
| 152 | u8 pad[sizeof(struct sockaddr_in6) - | ||
| 153 | sizeof(struct sockaddr)]; | ||
| 154 | }; | ||
| 155 | |||
| 141 | struct cma_work { | 156 | struct cma_work { |
| 142 | struct work_struct work; | 157 | struct work_struct work; |
| 143 | struct rdma_id_private *id; | 158 | struct rdma_id_private *id; |
| @@ -243,6 +258,11 @@ static inline void sdp_set_ip_ver(struct sdp_hh *hh, u8 ip_ver) | |||
| 243 | hh->ip_version = (ip_ver << 4) | (hh->ip_version & 0xF); | 258 | hh->ip_version = (ip_ver << 4) | (hh->ip_version & 0xF); |
| 244 | } | 259 | } |
| 245 | 260 | ||
| 261 | static inline int cma_is_ud_ps(enum rdma_port_space ps) | ||
| 262 | { | ||
| 263 | return (ps == RDMA_PS_UDP || ps == RDMA_PS_IPOIB); | ||
| 264 | } | ||
| 265 | |||
| 246 | static void cma_attach_to_dev(struct rdma_id_private *id_priv, | 266 | static void cma_attach_to_dev(struct rdma_id_private *id_priv, |
| 247 | struct cma_device *cma_dev) | 267 | struct cma_device *cma_dev) |
| 248 | { | 268 | { |
| @@ -265,19 +285,41 @@ static void cma_detach_from_dev(struct rdma_id_private *id_priv) | |||
| 265 | id_priv->cma_dev = NULL; | 285 | id_priv->cma_dev = NULL; |
| 266 | } | 286 | } |
| 267 | 287 | ||
| 288 | static int cma_set_qkey(struct ib_device *device, u8 port_num, | ||
| 289 | enum rdma_port_space ps, | ||
| 290 | struct rdma_dev_addr *dev_addr, u32 *qkey) | ||
| 291 | { | ||
| 292 | struct ib_sa_mcmember_rec rec; | ||
| 293 | int ret = 0; | ||
| 294 | |||
| 295 | switch (ps) { | ||
| 296 | case RDMA_PS_UDP: | ||
| 297 | *qkey = RDMA_UDP_QKEY; | ||
| 298 | break; | ||
| 299 | case RDMA_PS_IPOIB: | ||
| 300 | ib_addr_get_mgid(dev_addr, &rec.mgid); | ||
| 301 | ret = ib_sa_get_mcmember_rec(device, port_num, &rec.mgid, &rec); | ||
| 302 | *qkey = be32_to_cpu(rec.qkey); | ||
| 303 | break; | ||
| 304 | default: | ||
| 305 | break; | ||
| 306 | } | ||
| 307 | return ret; | ||
| 308 | } | ||
| 309 | |||
| 268 | static int cma_acquire_dev(struct rdma_id_private *id_priv) | 310 | static int cma_acquire_dev(struct rdma_id_private *id_priv) |
| 269 | { | 311 | { |
| 270 | enum rdma_node_type dev_type = id_priv->id.route.addr.dev_addr.dev_type; | 312 | struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; |
| 271 | struct cma_device *cma_dev; | 313 | struct cma_device *cma_dev; |
| 272 | union ib_gid gid; | 314 | union ib_gid gid; |
| 273 | int ret = -ENODEV; | 315 | int ret = -ENODEV; |
| 274 | 316 | ||
| 275 | switch (rdma_node_get_transport(dev_type)) { | 317 | switch (rdma_node_get_transport(dev_addr->dev_type)) { |
| 276 | case RDMA_TRANSPORT_IB: | 318 | case RDMA_TRANSPORT_IB: |
| 277 | ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid); | 319 | ib_addr_get_sgid(dev_addr, &gid); |
| 278 | break; | 320 | break; |
| 279 | case RDMA_TRANSPORT_IWARP: | 321 | case RDMA_TRANSPORT_IWARP: |
| 280 | iw_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid); | 322 | iw_addr_get_sgid(dev_addr, &gid); |
| 281 | break; | 323 | break; |
| 282 | default: | 324 | default: |
| 283 | return -ENODEV; | 325 | return -ENODEV; |
| @@ -287,7 +329,12 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv) | |||
| 287 | ret = ib_find_cached_gid(cma_dev->device, &gid, | 329 | ret = ib_find_cached_gid(cma_dev->device, &gid, |
| 288 | &id_priv->id.port_num, NULL); | 330 | &id_priv->id.port_num, NULL); |
| 289 | if (!ret) { | 331 | if (!ret) { |
| 290 | cma_attach_to_dev(id_priv, cma_dev); | 332 | ret = cma_set_qkey(cma_dev->device, |
| 333 | id_priv->id.port_num, | ||
| 334 | id_priv->id.ps, dev_addr, | ||
| 335 | &id_priv->qkey); | ||
| 336 | if (!ret) | ||
| 337 | cma_attach_to_dev(id_priv, cma_dev); | ||
| 291 | break; | 338 | break; |
| 292 | } | 339 | } |
| 293 | } | 340 | } |
| @@ -325,40 +372,50 @@ struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler, | |||
| 325 | init_waitqueue_head(&id_priv->wait_remove); | 372 | init_waitqueue_head(&id_priv->wait_remove); |
| 326 | atomic_set(&id_priv->dev_remove, 0); | 373 | atomic_set(&id_priv->dev_remove, 0); |
| 327 | INIT_LIST_HEAD(&id_priv->listen_list); | 374 | INIT_LIST_HEAD(&id_priv->listen_list); |
| 375 | INIT_LIST_HEAD(&id_priv->mc_list); | ||
| 328 | get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num); | 376 | get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num); |
| 329 | 377 | ||
| 330 | return &id_priv->id; | 378 | return &id_priv->id; |
| 331 | } | 379 | } |
| 332 | EXPORT_SYMBOL(rdma_create_id); | 380 | EXPORT_SYMBOL(rdma_create_id); |
| 333 | 381 | ||
| 334 | static int cma_init_ib_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) | 382 | static int cma_init_ud_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) |
| 335 | { | 383 | { |
| 336 | struct ib_qp_attr qp_attr; | 384 | struct ib_qp_attr qp_attr; |
| 337 | struct rdma_dev_addr *dev_addr; | 385 | int qp_attr_mask, ret; |
| 338 | int ret; | ||
| 339 | 386 | ||
| 340 | dev_addr = &id_priv->id.route.addr.dev_addr; | 387 | qp_attr.qp_state = IB_QPS_INIT; |
| 341 | ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num, | 388 | ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask); |
| 342 | ib_addr_get_pkey(dev_addr), | ||
| 343 | &qp_attr.pkey_index); | ||
| 344 | if (ret) | 389 | if (ret) |
| 345 | return ret; | 390 | return ret; |
| 346 | 391 | ||
| 347 | qp_attr.qp_state = IB_QPS_INIT; | 392 | ret = ib_modify_qp(qp, &qp_attr, qp_attr_mask); |
| 348 | qp_attr.qp_access_flags = 0; | 393 | if (ret) |
| 349 | qp_attr.port_num = id_priv->id.port_num; | 394 | return ret; |
| 350 | return ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS | | 395 | |
| 351 | IB_QP_PKEY_INDEX | IB_QP_PORT); | 396 | qp_attr.qp_state = IB_QPS_RTR; |
| 397 | ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE); | ||
| 398 | if (ret) | ||
| 399 | return ret; | ||
| 400 | |||
| 401 | qp_attr.qp_state = IB_QPS_RTS; | ||
| 402 | qp_attr.sq_psn = 0; | ||
| 403 | ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_SQ_PSN); | ||
| 404 | |||
| 405 | return ret; | ||
| 352 | } | 406 | } |
| 353 | 407 | ||
| 354 | static int cma_init_iw_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) | 408 | static int cma_init_conn_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) |
| 355 | { | 409 | { |
| 356 | struct ib_qp_attr qp_attr; | 410 | struct ib_qp_attr qp_attr; |
| 411 | int qp_attr_mask, ret; | ||
| 357 | 412 | ||
| 358 | qp_attr.qp_state = IB_QPS_INIT; | 413 | qp_attr.qp_state = IB_QPS_INIT; |
| 359 | qp_attr.qp_access_flags = IB_ACCESS_LOCAL_WRITE; | 414 | ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask); |
| 415 | if (ret) | ||
| 416 | return ret; | ||
| 360 | 417 | ||
| 361 | return ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS); | 418 | return ib_modify_qp(qp, &qp_attr, qp_attr_mask); |
| 362 | } | 419 | } |
| 363 | 420 | ||
| 364 | int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd, | 421 | int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd, |
| @@ -376,18 +433,10 @@ int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd, | |||
| 376 | if (IS_ERR(qp)) | 433 | if (IS_ERR(qp)) |
| 377 | return PTR_ERR(qp); | 434 | return PTR_ERR(qp); |
| 378 | 435 | ||
| 379 | switch (rdma_node_get_transport(id->device->node_type)) { | 436 | if (cma_is_ud_ps(id_priv->id.ps)) |
| 380 | case RDMA_TRANSPORT_IB: | 437 | ret = cma_init_ud_qp(id_priv, qp); |
| 381 | ret = cma_init_ib_qp(id_priv, qp); | 438 | else |
| 382 | break; | 439 | ret = cma_init_conn_qp(id_priv, qp); |
| 383 | case RDMA_TRANSPORT_IWARP: | ||
| 384 | ret = cma_init_iw_qp(id_priv, qp); | ||
| 385 | break; | ||
| 386 | default: | ||
| 387 | ret = -ENOSYS; | ||
| 388 | break; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (ret) | 440 | if (ret) |
| 392 | goto err; | 441 | goto err; |
| 393 | 442 | ||
| @@ -460,23 +509,55 @@ static int cma_modify_qp_err(struct rdma_cm_id *id) | |||
| 460 | return ib_modify_qp(id->qp, &qp_attr, IB_QP_STATE); | 509 | return ib_modify_qp(id->qp, &qp_attr, IB_QP_STATE); |
| 461 | } | 510 | } |
| 462 | 511 | ||
| 512 | static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv, | ||
| 513 | struct ib_qp_attr *qp_attr, int *qp_attr_mask) | ||
| 514 | { | ||
| 515 | struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; | ||
| 516 | int ret; | ||
| 517 | |||
| 518 | ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num, | ||
| 519 | ib_addr_get_pkey(dev_addr), | ||
| 520 | &qp_attr->pkey_index); | ||
| 521 | if (ret) | ||
| 522 | return ret; | ||
| 523 | |||
| 524 | qp_attr->port_num = id_priv->id.port_num; | ||
| 525 | *qp_attr_mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_PORT; | ||
| 526 | |||
| 527 | if (cma_is_ud_ps(id_priv->id.ps)) { | ||
| 528 | qp_attr->qkey = id_priv->qkey; | ||
| 529 | *qp_attr_mask |= IB_QP_QKEY; | ||
| 530 | } else { | ||
| 531 | qp_attr->qp_access_flags = 0; | ||
| 532 | *qp_attr_mask |= IB_QP_ACCESS_FLAGS; | ||
| 533 | } | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 463 | int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, | 537 | int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, |
| 464 | int *qp_attr_mask) | 538 | int *qp_attr_mask) |
| 465 | { | 539 | { |
| 466 | struct rdma_id_private *id_priv; | 540 | struct rdma_id_private *id_priv; |
| 467 | int ret; | 541 | int ret = 0; |
| 468 | 542 | ||
| 469 | id_priv = container_of(id, struct rdma_id_private, id); | 543 | id_priv = container_of(id, struct rdma_id_private, id); |
| 470 | switch (rdma_node_get_transport(id_priv->id.device->node_type)) { | 544 | switch (rdma_node_get_transport(id_priv->id.device->node_type)) { |
| 471 | case RDMA_TRANSPORT_IB: | 545 | case RDMA_TRANSPORT_IB: |
| 472 | ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, qp_attr, | 546 | if (!id_priv->cm_id.ib || cma_is_ud_ps(id_priv->id.ps)) |
| 473 | qp_attr_mask); | 547 | ret = cma_ib_init_qp_attr(id_priv, qp_attr, qp_attr_mask); |
| 548 | else | ||
| 549 | ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, qp_attr, | ||
| 550 | qp_attr_mask); | ||
| 474 | if (qp_attr->qp_state == IB_QPS_RTR) | 551 | if (qp_attr->qp_state == IB_QPS_RTR) |
| 475 | qp_attr->rq_psn = id_priv->seq_num; | 552 | qp_attr->rq_psn = id_priv->seq_num; |
| 476 | break; | 553 | break; |
| 477 | case RDMA_TRANSPORT_IWARP: | 554 | case RDMA_TRANSPORT_IWARP: |
| 478 | ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, | 555 | if (!id_priv->cm_id.iw) { |
| 479 | qp_attr_mask); | 556 | qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE; |
| 557 | *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; | ||
| 558 | } else | ||
| 559 | ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, | ||
| 560 | qp_attr_mask); | ||
| 480 | break; | 561 | break; |
| 481 | default: | 562 | default: |
| 482 | ret = -ENOSYS; | 563 | ret = -ENOSYS; |
| @@ -698,6 +779,19 @@ static void cma_release_port(struct rdma_id_private *id_priv) | |||
| 698 | mutex_unlock(&lock); | 779 | mutex_unlock(&lock); |
| 699 | } | 780 | } |
| 700 | 781 | ||
| 782 | static void cma_leave_mc_groups(struct rdma_id_private *id_priv) | ||
| 783 | { | ||
| 784 | struct cma_multicast *mc; | ||
| 785 | |||
| 786 | while (!list_empty(&id_priv->mc_list)) { | ||
| 787 | mc = container_of(id_priv->mc_list.next, | ||
| 788 | struct cma_multicast, list); | ||
| 789 | list_del(&mc->list); | ||
| 790 | ib_sa_free_multicast(mc->multicast.ib); | ||
| 791 | kfree(mc); | ||
| 792 | } | ||
| 793 | } | ||
| 794 | |||
| 701 | void rdma_destroy_id(struct rdma_cm_id *id) | 795 | void rdma_destroy_id(struct rdma_cm_id *id) |
| 702 | { | 796 | { |
| 703 | struct rdma_id_private *id_priv; | 797 | struct rdma_id_private *id_priv; |
| @@ -722,6 +816,7 @@ void rdma_destroy_id(struct rdma_cm_id *id) | |||
| 722 | default: | 816 | default: |
| 723 | break; | 817 | break; |
| 724 | } | 818 | } |
| 819 | cma_leave_mc_groups(id_priv); | ||
| 725 | mutex_lock(&lock); | 820 | mutex_lock(&lock); |
| 726 | cma_detach_from_dev(id_priv); | 821 | cma_detach_from_dev(id_priv); |
| 727 | } | 822 | } |
| @@ -972,7 +1067,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) | |||
| 972 | memset(&event, 0, sizeof event); | 1067 | memset(&event, 0, sizeof event); |
| 973 | offset = cma_user_data_offset(listen_id->id.ps); | 1068 | offset = cma_user_data_offset(listen_id->id.ps); |
| 974 | event.event = RDMA_CM_EVENT_CONNECT_REQUEST; | 1069 | event.event = RDMA_CM_EVENT_CONNECT_REQUEST; |
| 975 | if (listen_id->id.ps == RDMA_PS_UDP) { | 1070 | if (cma_is_ud_ps(listen_id->id.ps)) { |
| 976 | conn_id = cma_new_udp_id(&listen_id->id, ib_event); | 1071 | conn_id = cma_new_udp_id(&listen_id->id, ib_event); |
| 977 | event.param.ud.private_data = ib_event->private_data + offset; | 1072 | event.param.ud.private_data = ib_event->private_data + offset; |
| 978 | event.param.ud.private_data_len = | 1073 | event.param.ud.private_data_len = |
| @@ -1725,7 +1820,7 @@ static int cma_alloc_port(struct idr *ps, struct rdma_id_private *id_priv, | |||
| 1725 | struct rdma_bind_list *bind_list; | 1820 | struct rdma_bind_list *bind_list; |
| 1726 | int port, ret; | 1821 | int port, ret; |
| 1727 | 1822 | ||
| 1728 | bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL); | 1823 | bind_list = kmalloc(sizeof *bind_list, GFP_KERNEL); |
| 1729 | if (!bind_list) | 1824 | if (!bind_list) |
| 1730 | return -ENOMEM; | 1825 | return -ENOMEM; |
| 1731 | 1826 | ||
| @@ -1847,6 +1942,9 @@ static int cma_get_port(struct rdma_id_private *id_priv) | |||
| 1847 | case RDMA_PS_UDP: | 1942 | case RDMA_PS_UDP: |
| 1848 | ps = &udp_ps; | 1943 | ps = &udp_ps; |
| 1849 | break; | 1944 | break; |
| 1945 | case RDMA_PS_IPOIB: | ||
| 1946 | ps = &ipoib_ps; | ||
| 1947 | break; | ||
| 1850 | default: | 1948 | default: |
| 1851 | return -EPROTONOSUPPORT; | 1949 | return -EPROTONOSUPPORT; |
| 1852 | } | 1950 | } |
| @@ -1961,7 +2059,7 @@ static int cma_sidr_rep_handler(struct ib_cm_id *cm_id, | |||
| 1961 | event.status = ib_event->param.sidr_rep_rcvd.status; | 2059 | event.status = ib_event->param.sidr_rep_rcvd.status; |
| 1962 | break; | 2060 | break; |
| 1963 | } | 2061 | } |
| 1964 | if (rep->qkey != RDMA_UD_QKEY) { | 2062 | if (id_priv->qkey != rep->qkey) { |
| 1965 | event.event = RDMA_CM_EVENT_UNREACHABLE; | 2063 | event.event = RDMA_CM_EVENT_UNREACHABLE; |
| 1966 | event.status = -EINVAL; | 2064 | event.status = -EINVAL; |
| 1967 | break; | 2065 | break; |
| @@ -2160,7 +2258,7 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) | |||
| 2160 | 2258 | ||
| 2161 | switch (rdma_node_get_transport(id->device->node_type)) { | 2259 | switch (rdma_node_get_transport(id->device->node_type)) { |
| 2162 | case RDMA_TRANSPORT_IB: | 2260 | case RDMA_TRANSPORT_IB: |
| 2163 | if (id->ps == RDMA_PS_UDP) | 2261 | if (cma_is_ud_ps(id->ps)) |
| 2164 | ret = cma_resolve_ib_udp(id_priv, conn_param); | 2262 | ret = cma_resolve_ib_udp(id_priv, conn_param); |
| 2165 | else | 2263 | else |
| 2166 | ret = cma_connect_ib(id_priv, conn_param); | 2264 | ret = cma_connect_ib(id_priv, conn_param); |
| @@ -2256,7 +2354,7 @@ static int cma_send_sidr_rep(struct rdma_id_private *id_priv, | |||
| 2256 | rep.status = status; | 2354 | rep.status = status; |
| 2257 | if (status == IB_SIDR_SUCCESS) { | 2355 | if (status == IB_SIDR_SUCCESS) { |
| 2258 | rep.qp_num = id_priv->qp_num; | 2356 | rep.qp_num = id_priv->qp_num; |
| 2259 | rep.qkey = RDMA_UD_QKEY; | 2357 | rep.qkey = id_priv->qkey; |
| 2260 | } | 2358 | } |
| 2261 | rep.private_data = private_data; | 2359 | rep.private_data = private_data; |
| 2262 | rep.private_data_len = private_data_len; | 2360 | rep.private_data_len = private_data_len; |
| @@ -2280,7 +2378,7 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) | |||
| 2280 | 2378 | ||
| 2281 | switch (rdma_node_get_transport(id->device->node_type)) { | 2379 | switch (rdma_node_get_transport(id->device->node_type)) { |
| 2282 | case RDMA_TRANSPORT_IB: | 2380 | case RDMA_TRANSPORT_IB: |
| 2283 | if (id->ps == RDMA_PS_UDP) | 2381 | if (cma_is_ud_ps(id->ps)) |
| 2284 | ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS, | 2382 | ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS, |
| 2285 | conn_param->private_data, | 2383 | conn_param->private_data, |
| 2286 | conn_param->private_data_len); | 2384 | conn_param->private_data_len); |
| @@ -2341,7 +2439,7 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data, | |||
| 2341 | 2439 | ||
| 2342 | switch (rdma_node_get_transport(id->device->node_type)) { | 2440 | switch (rdma_node_get_transport(id->device->node_type)) { |
| 2343 | case RDMA_TRANSPORT_IB: | 2441 | case RDMA_TRANSPORT_IB: |
| 2344 | if (id->ps == RDMA_PS_UDP) | 2442 | if (cma_is_ud_ps(id->ps)) |
| 2345 | ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT, | 2443 | ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT, |
| 2346 | private_data, private_data_len); | 2444 | private_data, private_data_len); |
| 2347 | else | 2445 | else |
| @@ -2392,6 +2490,178 @@ out: | |||
| 2392 | } | 2490 | } |
| 2393 | EXPORT_SYMBOL(rdma_disconnect); | 2491 | EXPORT_SYMBOL(rdma_disconnect); |
| 2394 | 2492 | ||
| 2493 | static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast) | ||
| 2494 | { | ||
| 2495 | struct rdma_id_private *id_priv; | ||
| 2496 | struct cma_multicast *mc = multicast->context; | ||
| 2497 | struct rdma_cm_event event; | ||
| 2498 | int ret; | ||
| 2499 | |||
| 2500 | id_priv = mc->id_priv; | ||
| 2501 | atomic_inc(&id_priv->dev_remove); | ||
| 2502 | if (!cma_comp(id_priv, CMA_ADDR_BOUND) && | ||
| 2503 | !cma_comp(id_priv, CMA_ADDR_RESOLVED)) | ||
| 2504 | goto out; | ||
| 2505 | |||
| 2506 | if (!status && id_priv->id.qp) | ||
| 2507 | status = ib_attach_mcast(id_priv->id.qp, &multicast->rec.mgid, | ||
| 2508 | multicast->rec.mlid); | ||
| 2509 | |||
| 2510 | memset(&event, 0, sizeof event); | ||
| 2511 | event.status = status; | ||
| 2512 | event.param.ud.private_data = mc->context; | ||
| 2513 | if (!status) { | ||
| 2514 | event.event = RDMA_CM_EVENT_MULTICAST_JOIN; | ||
| 2515 | ib_init_ah_from_mcmember(id_priv->id.device, | ||
| 2516 | id_priv->id.port_num, &multicast->rec, | ||
| 2517 | &event.param.ud.ah_attr); | ||
| 2518 | event.param.ud.qp_num = 0xFFFFFF; | ||
| 2519 | event.param.ud.qkey = be32_to_cpu(multicast->rec.qkey); | ||
| 2520 | } else | ||
| 2521 | event.event = RDMA_CM_EVENT_MULTICAST_ERROR; | ||
| 2522 | |||
| 2523 | ret = id_priv->id.event_handler(&id_priv->id, &event); | ||
| 2524 | if (ret) { | ||
| 2525 | cma_exch(id_priv, CMA_DESTROYING); | ||
| 2526 | cma_release_remove(id_priv); | ||
| 2527 | rdma_destroy_id(&id_priv->id); | ||
| 2528 | return 0; | ||
| 2529 | } | ||
| 2530 | out: | ||
| 2531 | cma_release_remove(id_priv); | ||
| 2532 | return 0; | ||
| 2533 | } | ||
| 2534 | |||
| 2535 | static void cma_set_mgid(struct rdma_id_private *id_priv, | ||
| 2536 | struct sockaddr *addr, union ib_gid *mgid) | ||
| 2537 | { | ||
| 2538 | unsigned char mc_map[MAX_ADDR_LEN]; | ||
| 2539 | struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; | ||
| 2540 | struct sockaddr_in *sin = (struct sockaddr_in *) addr; | ||
| 2541 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr; | ||
| 2542 | |||
| 2543 | if (cma_any_addr(addr)) { | ||
| 2544 | memset(mgid, 0, sizeof *mgid); | ||
| 2545 | } else if ((addr->sa_family == AF_INET6) && | ||
| 2546 | ((be32_to_cpu(sin6->sin6_addr.s6_addr32[0]) & 0xFF10A01B) == | ||
| 2547 | 0xFF10A01B)) { | ||
| 2548 | /* IPv6 address is an SA assigned MGID. */ | ||
| 2549 | memcpy(mgid, &sin6->sin6_addr, sizeof *mgid); | ||
| 2550 | } else { | ||
| 2551 | ip_ib_mc_map(sin->sin_addr.s_addr, mc_map); | ||
| 2552 | if (id_priv->id.ps == RDMA_PS_UDP) | ||
| 2553 | mc_map[7] = 0x01; /* Use RDMA CM signature */ | ||
| 2554 | mc_map[8] = ib_addr_get_pkey(dev_addr) >> 8; | ||
| 2555 | mc_map[9] = (unsigned char) ib_addr_get_pkey(dev_addr); | ||
| 2556 | *mgid = *(union ib_gid *) (mc_map + 4); | ||
| 2557 | } | ||
| 2558 | } | ||
| 2559 | |||
| 2560 | static int cma_join_ib_multicast(struct rdma_id_private *id_priv, | ||
| 2561 | struct cma_multicast *mc) | ||
| 2562 | { | ||
| 2563 | struct ib_sa_mcmember_rec rec; | ||
| 2564 | struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; | ||
| 2565 | ib_sa_comp_mask comp_mask; | ||
| 2566 | int ret; | ||
| 2567 | |||
| 2568 | ib_addr_get_mgid(dev_addr, &rec.mgid); | ||
| 2569 | ret = ib_sa_get_mcmember_rec(id_priv->id.device, id_priv->id.port_num, | ||
| 2570 | &rec.mgid, &rec); | ||
| 2571 | if (ret) | ||
| 2572 | return ret; | ||
| 2573 | |||
| 2574 | cma_set_mgid(id_priv, &mc->addr, &rec.mgid); | ||
| 2575 | if (id_priv->id.ps == RDMA_PS_UDP) | ||
| 2576 | rec.qkey = cpu_to_be32(RDMA_UDP_QKEY); | ||
| 2577 | ib_addr_get_sgid(dev_addr, &rec.port_gid); | ||
| 2578 | rec.pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr)); | ||
| 2579 | rec.join_state = 1; | ||
| 2580 | |||
| 2581 | comp_mask = IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | | ||
| 2582 | IB_SA_MCMEMBER_REC_PKEY | IB_SA_MCMEMBER_REC_JOIN_STATE | | ||
| 2583 | IB_SA_MCMEMBER_REC_QKEY | IB_SA_MCMEMBER_REC_SL | | ||
| 2584 | IB_SA_MCMEMBER_REC_FLOW_LABEL | | ||
| 2585 | IB_SA_MCMEMBER_REC_TRAFFIC_CLASS; | ||
| 2586 | |||
| 2587 | mc->multicast.ib = ib_sa_join_multicast(&sa_client, id_priv->id.device, | ||
| 2588 | id_priv->id.port_num, &rec, | ||
| 2589 | comp_mask, GFP_KERNEL, | ||
| 2590 | cma_ib_mc_handler, mc); | ||
| 2591 | if (IS_ERR(mc->multicast.ib)) | ||
| 2592 | return PTR_ERR(mc->multicast.ib); | ||
| 2593 | |||
| 2594 | return 0; | ||
| 2595 | } | ||
| 2596 | |||
| 2597 | int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr, | ||
| 2598 | void *context) | ||
| 2599 | { | ||
| 2600 | struct rdma_id_private *id_priv; | ||
| 2601 | struct cma_multicast *mc; | ||
| 2602 | int ret; | ||
| 2603 | |||
| 2604 | id_priv = container_of(id, struct rdma_id_private, id); | ||
| 2605 | if (!cma_comp(id_priv, CMA_ADDR_BOUND) && | ||
| 2606 | !cma_comp(id_priv, CMA_ADDR_RESOLVED)) | ||
| 2607 | return -EINVAL; | ||
| 2608 | |||
| 2609 | mc = kmalloc(sizeof *mc, GFP_KERNEL); | ||
| 2610 | if (!mc) | ||
| 2611 | return -ENOMEM; | ||
| 2612 | |||
| 2613 | memcpy(&mc->addr, addr, ip_addr_size(addr)); | ||
| 2614 | mc->context = context; | ||
| 2615 | mc->id_priv = id_priv; | ||
| 2616 | |||
| 2617 | spin_lock(&id_priv->lock); | ||
| 2618 | list_add(&mc->list, &id_priv->mc_list); | ||
| 2619 | spin_unlock(&id_priv->lock); | ||
| 2620 | |||
| 2621 | switch (rdma_node_get_transport(id->device->node_type)) { | ||
| 2622 | case RDMA_TRANSPORT_IB: | ||
| 2623 | ret = cma_join_ib_multicast(id_priv, mc); | ||
| 2624 | break; | ||
| 2625 | default: | ||
| 2626 | ret = -ENOSYS; | ||
| 2627 | break; | ||
| 2628 | } | ||
| 2629 | |||
| 2630 | if (ret) { | ||
| 2631 | spin_lock_irq(&id_priv->lock); | ||
| 2632 | list_del(&mc->list); | ||
| 2633 | spin_unlock_irq(&id_priv->lock); | ||
| 2634 | kfree(mc); | ||
| 2635 | } | ||
| 2636 | return ret; | ||
| 2637 | } | ||
| 2638 | EXPORT_SYMBOL(rdma_join_multicast); | ||
| 2639 | |||
| 2640 | void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr) | ||
| 2641 | { | ||
| 2642 | struct rdma_id_private *id_priv; | ||
| 2643 | struct cma_multicast *mc; | ||
| 2644 | |||
| 2645 | id_priv = container_of(id, struct rdma_id_private, id); | ||
| 2646 | spin_lock_irq(&id_priv->lock); | ||
| 2647 | list_for_each_entry(mc, &id_priv->mc_list, list) { | ||
| 2648 | if (!memcmp(&mc->addr, addr, ip_addr_size(addr))) { | ||
| 2649 | list_del(&mc->list); | ||
| 2650 | spin_unlock_irq(&id_priv->lock); | ||
| 2651 | |||
| 2652 | if (id->qp) | ||
| 2653 | ib_detach_mcast(id->qp, | ||
| 2654 | &mc->multicast.ib->rec.mgid, | ||
| 2655 | mc->multicast.ib->rec.mlid); | ||
| 2656 | ib_sa_free_multicast(mc->multicast.ib); | ||
| 2657 | kfree(mc); | ||
| 2658 | return; | ||
| 2659 | } | ||
| 2660 | } | ||
| 2661 | spin_unlock_irq(&id_priv->lock); | ||
| 2662 | } | ||
| 2663 | EXPORT_SYMBOL(rdma_leave_multicast); | ||
| 2664 | |||
| 2395 | static void cma_add_one(struct ib_device *device) | 2665 | static void cma_add_one(struct ib_device *device) |
| 2396 | { | 2666 | { |
| 2397 | struct cma_device *cma_dev; | 2667 | struct cma_device *cma_dev; |
| @@ -2522,6 +2792,7 @@ static void cma_cleanup(void) | |||
| 2522 | idr_destroy(&sdp_ps); | 2792 | idr_destroy(&sdp_ps); |
| 2523 | idr_destroy(&tcp_ps); | 2793 | idr_destroy(&tcp_ps); |
| 2524 | idr_destroy(&udp_ps); | 2794 | idr_destroy(&udp_ps); |
| 2795 | idr_destroy(&ipoib_ps); | ||
| 2525 | } | 2796 | } |
| 2526 | 2797 | ||
| 2527 | module_init(cma_init); | 2798 | module_init(cma_init); |
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 6b81b98961c7..b516b93b8550 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c | |||
| @@ -70,10 +70,24 @@ struct ucma_context { | |||
| 70 | u64 uid; | 70 | u64 uid; |
| 71 | 71 | ||
| 72 | struct list_head list; | 72 | struct list_head list; |
| 73 | struct list_head mc_list; | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct ucma_multicast { | ||
| 77 | struct ucma_context *ctx; | ||
| 78 | int id; | ||
| 79 | int events_reported; | ||
| 80 | |||
| 81 | u64 uid; | ||
| 82 | struct list_head list; | ||
| 83 | struct sockaddr addr; | ||
| 84 | u8 pad[sizeof(struct sockaddr_in6) - | ||
| 85 | sizeof(struct sockaddr)]; | ||
| 73 | }; | 86 | }; |
| 74 | 87 | ||
| 75 | struct ucma_event { | 88 | struct ucma_event { |
| 76 | struct ucma_context *ctx; | 89 | struct ucma_context *ctx; |
| 90 | struct ucma_multicast *mc; | ||
| 77 | struct list_head list; | 91 | struct list_head list; |
| 78 | struct rdma_cm_id *cm_id; | 92 | struct rdma_cm_id *cm_id; |
| 79 | struct rdma_ucm_event_resp resp; | 93 | struct rdma_ucm_event_resp resp; |
| @@ -81,6 +95,7 @@ struct ucma_event { | |||
| 81 | 95 | ||
| 82 | static DEFINE_MUTEX(mut); | 96 | static DEFINE_MUTEX(mut); |
| 83 | static DEFINE_IDR(ctx_idr); | 97 | static DEFINE_IDR(ctx_idr); |
| 98 | static DEFINE_IDR(multicast_idr); | ||
| 84 | 99 | ||
| 85 | static inline struct ucma_context *_ucma_find_context(int id, | 100 | static inline struct ucma_context *_ucma_find_context(int id, |
| 86 | struct ucma_file *file) | 101 | struct ucma_file *file) |
| @@ -124,6 +139,7 @@ static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file) | |||
| 124 | 139 | ||
| 125 | atomic_set(&ctx->ref, 1); | 140 | atomic_set(&ctx->ref, 1); |
| 126 | init_completion(&ctx->comp); | 141 | init_completion(&ctx->comp); |
| 142 | INIT_LIST_HEAD(&ctx->mc_list); | ||
| 127 | ctx->file = file; | 143 | ctx->file = file; |
| 128 | 144 | ||
| 129 | do { | 145 | do { |
| @@ -147,6 +163,37 @@ error: | |||
| 147 | return NULL; | 163 | return NULL; |
| 148 | } | 164 | } |
| 149 | 165 | ||
| 166 | static struct ucma_multicast* ucma_alloc_multicast(struct ucma_context *ctx) | ||
| 167 | { | ||
| 168 | struct ucma_multicast *mc; | ||
| 169 | int ret; | ||
| 170 | |||
| 171 | mc = kzalloc(sizeof(*mc), GFP_KERNEL); | ||
| 172 | if (!mc) | ||
| 173 | return NULL; | ||
| 174 | |||
| 175 | do { | ||
| 176 | ret = idr_pre_get(&multicast_idr, GFP_KERNEL); | ||
| 177 | if (!ret) | ||
| 178 | goto error; | ||
| 179 | |||
| 180 | mutex_lock(&mut); | ||
| 181 | ret = idr_get_new(&multicast_idr, mc, &mc->id); | ||
| 182 | mutex_unlock(&mut); | ||
| 183 | } while (ret == -EAGAIN); | ||
| 184 | |||
| 185 | if (ret) | ||
| 186 | goto error; | ||
| 187 | |||
| 188 | mc->ctx = ctx; | ||
| 189 | list_add_tail(&mc->list, &ctx->mc_list); | ||
| 190 | return mc; | ||
| 191 | |||
| 192 | error: | ||
| 193 | kfree(mc); | ||
| 194 | return NULL; | ||
| 195 | } | ||
| 196 | |||
| 150 | static void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst, | 197 | static void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst, |
| 151 | struct rdma_conn_param *src) | 198 | struct rdma_conn_param *src) |
| 152 | { | 199 | { |
| @@ -180,8 +227,19 @@ static void ucma_set_event_context(struct ucma_context *ctx, | |||
| 180 | struct ucma_event *uevent) | 227 | struct ucma_event *uevent) |
| 181 | { | 228 | { |
| 182 | uevent->ctx = ctx; | 229 | uevent->ctx = ctx; |
| 183 | uevent->resp.uid = ctx->uid; | 230 | switch (event->event) { |
| 184 | uevent->resp.id = ctx->id; | 231 | case RDMA_CM_EVENT_MULTICAST_JOIN: |
| 232 | case RDMA_CM_EVENT_MULTICAST_ERROR: | ||
| 233 | uevent->mc = (struct ucma_multicast *) | ||
| 234 | event->param.ud.private_data; | ||
| 235 | uevent->resp.uid = uevent->mc->uid; | ||
| 236 | uevent->resp.id = uevent->mc->id; | ||
| 237 | break; | ||
| 238 | default: | ||
| 239 | uevent->resp.uid = ctx->uid; | ||
| 240 | uevent->resp.id = ctx->id; | ||
| 241 | break; | ||
| 242 | } | ||
| 185 | } | 243 | } |
| 186 | 244 | ||
| 187 | static int ucma_event_handler(struct rdma_cm_id *cm_id, | 245 | static int ucma_event_handler(struct rdma_cm_id *cm_id, |
| @@ -199,7 +257,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id, | |||
| 199 | ucma_set_event_context(ctx, event, uevent); | 257 | ucma_set_event_context(ctx, event, uevent); |
| 200 | uevent->resp.event = event->event; | 258 | uevent->resp.event = event->event; |
| 201 | uevent->resp.status = event->status; | 259 | uevent->resp.status = event->status; |
| 202 | if (cm_id->ps == RDMA_PS_UDP) | 260 | if (cm_id->ps == RDMA_PS_UDP || cm_id->ps == RDMA_PS_IPOIB) |
| 203 | ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud); | 261 | ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud); |
| 204 | else | 262 | else |
| 205 | ucma_copy_conn_event(&uevent->resp.param.conn, | 263 | ucma_copy_conn_event(&uevent->resp.param.conn, |
| @@ -290,6 +348,8 @@ static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf, | |||
| 290 | 348 | ||
| 291 | list_del(&uevent->list); | 349 | list_del(&uevent->list); |
| 292 | uevent->ctx->events_reported++; | 350 | uevent->ctx->events_reported++; |
| 351 | if (uevent->mc) | ||
| 352 | uevent->mc->events_reported++; | ||
| 293 | kfree(uevent); | 353 | kfree(uevent); |
| 294 | done: | 354 | done: |
| 295 | mutex_unlock(&file->mut); | 355 | mutex_unlock(&file->mut); |
| @@ -342,6 +402,19 @@ err1: | |||
| 342 | return ret; | 402 | return ret; |
| 343 | } | 403 | } |
| 344 | 404 | ||
| 405 | static void ucma_cleanup_multicast(struct ucma_context *ctx) | ||
| 406 | { | ||
| 407 | struct ucma_multicast *mc, *tmp; | ||
| 408 | |||
| 409 | mutex_lock(&mut); | ||
| 410 | list_for_each_entry_safe(mc, tmp, &ctx->mc_list, list) { | ||
| 411 | list_del(&mc->list); | ||
| 412 | idr_remove(&multicast_idr, mc->id); | ||
| 413 | kfree(mc); | ||
| 414 | } | ||
| 415 | mutex_unlock(&mut); | ||
| 416 | } | ||
| 417 | |||
| 345 | static void ucma_cleanup_events(struct ucma_context *ctx) | 418 | static void ucma_cleanup_events(struct ucma_context *ctx) |
| 346 | { | 419 | { |
| 347 | struct ucma_event *uevent, *tmp; | 420 | struct ucma_event *uevent, *tmp; |
| @@ -360,6 +433,19 @@ static void ucma_cleanup_events(struct ucma_context *ctx) | |||
| 360 | } | 433 | } |
| 361 | } | 434 | } |
| 362 | 435 | ||
| 436 | static void ucma_cleanup_mc_events(struct ucma_multicast *mc) | ||
| 437 | { | ||
| 438 | struct ucma_event *uevent, *tmp; | ||
| 439 | |||
| 440 | list_for_each_entry_safe(uevent, tmp, &mc->ctx->file->event_list, list) { | ||
| 441 | if (uevent->mc != mc) | ||
| 442 | continue; | ||
| 443 | |||
| 444 | list_del(&uevent->list); | ||
| 445 | kfree(uevent); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 363 | static int ucma_free_ctx(struct ucma_context *ctx) | 449 | static int ucma_free_ctx(struct ucma_context *ctx) |
| 364 | { | 450 | { |
| 365 | int events_reported; | 451 | int events_reported; |
| @@ -367,6 +453,8 @@ static int ucma_free_ctx(struct ucma_context *ctx) | |||
| 367 | /* No new events will be generated after destroying the id. */ | 453 | /* No new events will be generated after destroying the id. */ |
| 368 | rdma_destroy_id(ctx->cm_id); | 454 | rdma_destroy_id(ctx->cm_id); |
| 369 | 455 | ||
| 456 | ucma_cleanup_multicast(ctx); | ||
| 457 | |||
| 370 | /* Cleanup events not yet reported to the user. */ | 458 | /* Cleanup events not yet reported to the user. */ |
| 371 | mutex_lock(&ctx->file->mut); | 459 | mutex_lock(&ctx->file->mut); |
| 372 | ucma_cleanup_events(ctx); | 460 | ucma_cleanup_events(ctx); |
| @@ -731,6 +819,114 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, | |||
| 731 | return ret; | 819 | return ret; |
| 732 | } | 820 | } |
| 733 | 821 | ||
| 822 | static ssize_t ucma_join_multicast(struct ucma_file *file, | ||
| 823 | const char __user *inbuf, | ||
| 824 | int in_len, int out_len) | ||
| 825 | { | ||
| 826 | struct rdma_ucm_join_mcast cmd; | ||
| 827 | struct rdma_ucm_create_id_resp resp; | ||
| 828 | struct ucma_context *ctx; | ||
| 829 | struct ucma_multicast *mc; | ||
| 830 | int ret; | ||
| 831 | |||
| 832 | if (out_len < sizeof(resp)) | ||
| 833 | return -ENOSPC; | ||
| 834 | |||
| 835 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | ||
| 836 | return -EFAULT; | ||
| 837 | |||
| 838 | ctx = ucma_get_ctx(file, cmd.id); | ||
| 839 | if (IS_ERR(ctx)) | ||
| 840 | return PTR_ERR(ctx); | ||
| 841 | |||
| 842 | mutex_lock(&file->mut); | ||
| 843 | mc = ucma_alloc_multicast(ctx); | ||
| 844 | if (IS_ERR(mc)) { | ||
| 845 | ret = PTR_ERR(mc); | ||
| 846 | goto err1; | ||
| 847 | } | ||
| 848 | |||
| 849 | mc->uid = cmd.uid; | ||
| 850 | memcpy(&mc->addr, &cmd.addr, sizeof cmd.addr); | ||
| 851 | ret = rdma_join_multicast(ctx->cm_id, &mc->addr, mc); | ||
| 852 | if (ret) | ||
| 853 | goto err2; | ||
| 854 | |||
| 855 | resp.id = mc->id; | ||
| 856 | if (copy_to_user((void __user *)(unsigned long)cmd.response, | ||
| 857 | &resp, sizeof(resp))) { | ||
| 858 | ret = -EFAULT; | ||
| 859 | goto err3; | ||
| 860 | } | ||
| 861 | |||
| 862 | mutex_unlock(&file->mut); | ||
| 863 | ucma_put_ctx(ctx); | ||
| 864 | return 0; | ||
| 865 | |||
| 866 | err3: | ||
| 867 | rdma_leave_multicast(ctx->cm_id, &mc->addr); | ||
| 868 | ucma_cleanup_mc_events(mc); | ||
| 869 | err2: | ||
| 870 | mutex_lock(&mut); | ||
| 871 | idr_remove(&multicast_idr, mc->id); | ||
| 872 | mutex_unlock(&mut); | ||
| 873 | list_del(&mc->list); | ||
| 874 | kfree(mc); | ||
| 875 | err1: | ||
| 876 | mutex_unlock(&file->mut); | ||
| 877 | ucma_put_ctx(ctx); | ||
| 878 | return ret; | ||
| 879 | } | ||
| 880 | |||
| 881 | static ssize_t ucma_leave_multicast(struct ucma_file *file, | ||
| 882 | const char __user *inbuf, | ||
| 883 | int in_len, int out_len) | ||
| 884 | { | ||
| 885 | struct rdma_ucm_destroy_id cmd; | ||
| 886 | struct rdma_ucm_destroy_id_resp resp; | ||
| 887 | struct ucma_multicast *mc; | ||
| 888 | int ret = 0; | ||
| 889 | |||
| 890 | if (out_len < sizeof(resp)) | ||
| 891 | return -ENOSPC; | ||
| 892 | |||
| 893 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | ||
| 894 | return -EFAULT; | ||
| 895 | |||
| 896 | mutex_lock(&mut); | ||
| 897 | mc = idr_find(&multicast_idr, cmd.id); | ||
| 898 | if (!mc) | ||
| 899 | mc = ERR_PTR(-ENOENT); | ||
| 900 | else if (mc->ctx->file != file) | ||
| 901 | mc = ERR_PTR(-EINVAL); | ||
| 902 | else { | ||
| 903 | idr_remove(&multicast_idr, mc->id); | ||
| 904 | atomic_inc(&mc->ctx->ref); | ||
| 905 | } | ||
| 906 | mutex_unlock(&mut); | ||
| 907 | |||
| 908 | if (IS_ERR(mc)) { | ||
| 909 | ret = PTR_ERR(mc); | ||
| 910 | goto out; | ||
| 911 | } | ||
| 912 | |||
| 913 | rdma_leave_multicast(mc->ctx->cm_id, &mc->addr); | ||
| 914 | mutex_lock(&mc->ctx->file->mut); | ||
| 915 | ucma_cleanup_mc_events(mc); | ||
| 916 | list_del(&mc->list); | ||
| 917 | mutex_unlock(&mc->ctx->file->mut); | ||
| 918 | |||
| 919 | ucma_put_ctx(mc->ctx); | ||
| 920 | resp.events_reported = mc->events_reported; | ||
| 921 | kfree(mc); | ||
| 922 | |||
| 923 | if (copy_to_user((void __user *)(unsigned long)cmd.response, | ||
| 924 | &resp, sizeof(resp))) | ||
| 925 | ret = -EFAULT; | ||
| 926 | out: | ||
| 927 | return ret; | ||
| 928 | } | ||
| 929 | |||
| 734 | static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, | 930 | static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, |
| 735 | const char __user *inbuf, | 931 | const char __user *inbuf, |
| 736 | int in_len, int out_len) = { | 932 | int in_len, int out_len) = { |
| @@ -750,6 +946,8 @@ static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, | |||
| 750 | [RDMA_USER_CM_CMD_GET_OPTION] = NULL, | 946 | [RDMA_USER_CM_CMD_GET_OPTION] = NULL, |
| 751 | [RDMA_USER_CM_CMD_SET_OPTION] = NULL, | 947 | [RDMA_USER_CM_CMD_SET_OPTION] = NULL, |
| 752 | [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, | 948 | [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, |
| 949 | [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast, | ||
| 950 | [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast, | ||
| 753 | }; | 951 | }; |
| 754 | 952 | ||
| 755 | static ssize_t ucma_write(struct file *filp, const char __user *buf, | 953 | static ssize_t ucma_write(struct file *filp, const char __user *buf, |
diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index 36cd8a8526a0..2d6a7705eae7 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h | |||
| @@ -52,10 +52,13 @@ enum rdma_cm_event_type { | |||
| 52 | RDMA_CM_EVENT_ESTABLISHED, | 52 | RDMA_CM_EVENT_ESTABLISHED, |
| 53 | RDMA_CM_EVENT_DISCONNECTED, | 53 | RDMA_CM_EVENT_DISCONNECTED, |
| 54 | RDMA_CM_EVENT_DEVICE_REMOVAL, | 54 | RDMA_CM_EVENT_DEVICE_REMOVAL, |
| 55 | RDMA_CM_EVENT_MULTICAST_JOIN, | ||
| 56 | RDMA_CM_EVENT_MULTICAST_ERROR | ||
| 55 | }; | 57 | }; |
| 56 | 58 | ||
| 57 | enum rdma_port_space { | 59 | enum rdma_port_space { |
| 58 | RDMA_PS_SDP = 0x0001, | 60 | RDMA_PS_SDP = 0x0001, |
| 61 | RDMA_PS_IPOIB= 0x0002, | ||
| 59 | RDMA_PS_TCP = 0x0106, | 62 | RDMA_PS_TCP = 0x0106, |
| 60 | RDMA_PS_UDP = 0x0111, | 63 | RDMA_PS_UDP = 0x0111, |
| 61 | RDMA_PS_SCTP = 0x0183 | 64 | RDMA_PS_SCTP = 0x0183 |
| @@ -294,5 +297,21 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data, | |||
| 294 | */ | 297 | */ |
| 295 | int rdma_disconnect(struct rdma_cm_id *id); | 298 | int rdma_disconnect(struct rdma_cm_id *id); |
| 296 | 299 | ||
| 297 | #endif /* RDMA_CM_H */ | 300 | /** |
| 301 | * rdma_join_multicast - Join the multicast group specified by the given | ||
| 302 | * address. | ||
| 303 | * @id: Communication identifier associated with the request. | ||
| 304 | * @addr: Multicast address identifying the group to join. | ||
| 305 | * @context: User-defined context associated with the join request, returned | ||
| 306 | * to the user through the private_data pointer in multicast events. | ||
| 307 | */ | ||
| 308 | int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr, | ||
| 309 | void *context); | ||
| 298 | 310 | ||
| 311 | /** | ||
| 312 | * rdma_leave_multicast - Leave the multicast group specified by the given | ||
| 313 | * address. | ||
| 314 | */ | ||
| 315 | void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr); | ||
| 316 | |||
| 317 | #endif /* RDMA_CM_H */ | ||
diff --git a/include/rdma/rdma_cm_ib.h b/include/rdma/rdma_cm_ib.h index 9b176df1d667..950424b38f16 100644 --- a/include/rdma/rdma_cm_ib.h +++ b/include/rdma/rdma_cm_ib.h | |||
| @@ -44,7 +44,7 @@ | |||
| 44 | int rdma_set_ib_paths(struct rdma_cm_id *id, | 44 | int rdma_set_ib_paths(struct rdma_cm_id *id, |
| 45 | struct ib_sa_path_rec *path_rec, int num_paths); | 45 | struct ib_sa_path_rec *path_rec, int num_paths); |
| 46 | 46 | ||
| 47 | /* Global qkey for UD QPs and multicast groups. */ | 47 | /* Global qkey for UDP QPs and multicast groups. */ |
| 48 | #define RDMA_UD_QKEY 0x01234567 | 48 | #define RDMA_UDP_QKEY 0x01234567 |
| 49 | 49 | ||
| 50 | #endif /* RDMA_CM_IB_H */ | 50 | #endif /* RDMA_CM_IB_H */ |
diff --git a/include/rdma/rdma_user_cm.h b/include/rdma/rdma_user_cm.h index 9572ab8eeac1..f632b0c007c9 100644 --- a/include/rdma/rdma_user_cm.h +++ b/include/rdma/rdma_user_cm.h | |||
| @@ -38,7 +38,7 @@ | |||
| 38 | #include <rdma/ib_user_verbs.h> | 38 | #include <rdma/ib_user_verbs.h> |
| 39 | #include <rdma/ib_user_sa.h> | 39 | #include <rdma/ib_user_sa.h> |
| 40 | 40 | ||
| 41 | #define RDMA_USER_CM_ABI_VERSION 3 | 41 | #define RDMA_USER_CM_ABI_VERSION 4 |
| 42 | 42 | ||
| 43 | #define RDMA_MAX_PRIVATE_DATA 256 | 43 | #define RDMA_MAX_PRIVATE_DATA 256 |
| 44 | 44 | ||
| @@ -58,7 +58,9 @@ enum { | |||
| 58 | RDMA_USER_CM_CMD_GET_EVENT, | 58 | RDMA_USER_CM_CMD_GET_EVENT, |
| 59 | RDMA_USER_CM_CMD_GET_OPTION, | 59 | RDMA_USER_CM_CMD_GET_OPTION, |
| 60 | RDMA_USER_CM_CMD_SET_OPTION, | 60 | RDMA_USER_CM_CMD_SET_OPTION, |
| 61 | RDMA_USER_CM_CMD_NOTIFY | 61 | RDMA_USER_CM_CMD_NOTIFY, |
| 62 | RDMA_USER_CM_CMD_JOIN_MCAST, | ||
| 63 | RDMA_USER_CM_CMD_LEAVE_MCAST | ||
| 62 | }; | 64 | }; |
| 63 | 65 | ||
| 64 | /* | 66 | /* |
| @@ -188,6 +190,13 @@ struct rdma_ucm_notify { | |||
| 188 | __u32 event; | 190 | __u32 event; |
| 189 | }; | 191 | }; |
| 190 | 192 | ||
| 193 | struct rdma_ucm_join_mcast { | ||
| 194 | __u64 response; /* rdma_ucm_create_id_resp */ | ||
| 195 | __u64 uid; | ||
| 196 | struct sockaddr_in6 addr; | ||
| 197 | __u32 id; | ||
| 198 | }; | ||
| 199 | |||
| 191 | struct rdma_ucm_get_event { | 200 | struct rdma_ucm_get_event { |
| 192 | __u64 response; | 201 | __u64 response; |
| 193 | }; | 202 | }; |
