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 /drivers/infiniband/core/ucma.c | |
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>
Diffstat (limited to 'drivers/infiniband/core/ucma.c')
-rw-r--r-- | drivers/infiniband/core/ucma.c | 204 |
1 files changed, 201 insertions, 3 deletions
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, |