diff options
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/ucma.c | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 90d675ad9ec8..15937eb38aae 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c | |||
@@ -31,6 +31,7 @@ | |||
31 | */ | 31 | */ |
32 | 32 | ||
33 | #include <linux/completion.h> | 33 | #include <linux/completion.h> |
34 | #include <linux/file.h> | ||
34 | #include <linux/mutex.h> | 35 | #include <linux/mutex.h> |
35 | #include <linux/poll.h> | 36 | #include <linux/poll.h> |
36 | #include <linux/idr.h> | 37 | #include <linux/idr.h> |
@@ -991,6 +992,96 @@ out: | |||
991 | return ret; | 992 | return ret; |
992 | } | 993 | } |
993 | 994 | ||
995 | static void ucma_lock_files(struct ucma_file *file1, struct ucma_file *file2) | ||
996 | { | ||
997 | /* Acquire mutex's based on pointer comparison to prevent deadlock. */ | ||
998 | if (file1 < file2) { | ||
999 | mutex_lock(&file1->mut); | ||
1000 | mutex_lock(&file2->mut); | ||
1001 | } else { | ||
1002 | mutex_lock(&file2->mut); | ||
1003 | mutex_lock(&file1->mut); | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | static void ucma_unlock_files(struct ucma_file *file1, struct ucma_file *file2) | ||
1008 | { | ||
1009 | if (file1 < file2) { | ||
1010 | mutex_unlock(&file2->mut); | ||
1011 | mutex_unlock(&file1->mut); | ||
1012 | } else { | ||
1013 | mutex_unlock(&file1->mut); | ||
1014 | mutex_unlock(&file2->mut); | ||
1015 | } | ||
1016 | } | ||
1017 | |||
1018 | static void ucma_move_events(struct ucma_context *ctx, struct ucma_file *file) | ||
1019 | { | ||
1020 | struct ucma_event *uevent, *tmp; | ||
1021 | |||
1022 | list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) | ||
1023 | if (uevent->ctx == ctx) | ||
1024 | list_move_tail(&uevent->list, &file->event_list); | ||
1025 | } | ||
1026 | |||
1027 | static ssize_t ucma_migrate_id(struct ucma_file *new_file, | ||
1028 | const char __user *inbuf, | ||
1029 | int in_len, int out_len) | ||
1030 | { | ||
1031 | struct rdma_ucm_migrate_id cmd; | ||
1032 | struct rdma_ucm_migrate_resp resp; | ||
1033 | struct ucma_context *ctx; | ||
1034 | struct file *filp; | ||
1035 | struct ucma_file *cur_file; | ||
1036 | int ret = 0; | ||
1037 | |||
1038 | if (copy_from_user(&cmd, inbuf, sizeof(cmd))) | ||
1039 | return -EFAULT; | ||
1040 | |||
1041 | /* Get current fd to protect against it being closed */ | ||
1042 | filp = fget(cmd.fd); | ||
1043 | if (!filp) | ||
1044 | return -ENOENT; | ||
1045 | |||
1046 | /* Validate current fd and prevent destruction of id. */ | ||
1047 | ctx = ucma_get_ctx(filp->private_data, cmd.id); | ||
1048 | if (IS_ERR(ctx)) { | ||
1049 | ret = PTR_ERR(ctx); | ||
1050 | goto file_put; | ||
1051 | } | ||
1052 | |||
1053 | cur_file = ctx->file; | ||
1054 | if (cur_file == new_file) { | ||
1055 | resp.events_reported = ctx->events_reported; | ||
1056 | goto response; | ||
1057 | } | ||
1058 | |||
1059 | /* | ||
1060 | * Migrate events between fd's, maintaining order, and avoiding new | ||
1061 | * events being added before existing events. | ||
1062 | */ | ||
1063 | ucma_lock_files(cur_file, new_file); | ||
1064 | mutex_lock(&mut); | ||
1065 | |||
1066 | list_move_tail(&ctx->list, &new_file->ctx_list); | ||
1067 | ucma_move_events(ctx, new_file); | ||
1068 | ctx->file = new_file; | ||
1069 | resp.events_reported = ctx->events_reported; | ||
1070 | |||
1071 | mutex_unlock(&mut); | ||
1072 | ucma_unlock_files(cur_file, new_file); | ||
1073 | |||
1074 | response: | ||
1075 | if (copy_to_user((void __user *)(unsigned long)cmd.response, | ||
1076 | &resp, sizeof(resp))) | ||
1077 | ret = -EFAULT; | ||
1078 | |||
1079 | ucma_put_ctx(ctx); | ||
1080 | file_put: | ||
1081 | fput(filp); | ||
1082 | return ret; | ||
1083 | } | ||
1084 | |||
994 | static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, | 1085 | static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, |
995 | const char __user *inbuf, | 1086 | const char __user *inbuf, |
996 | int in_len, int out_len) = { | 1087 | int in_len, int out_len) = { |
@@ -1012,6 +1103,7 @@ static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, | |||
1012 | [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, | 1103 | [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, |
1013 | [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast, | 1104 | [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast, |
1014 | [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast, | 1105 | [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast, |
1106 | [RDMA_USER_CM_CMD_MIGRATE_ID] = ucma_migrate_id | ||
1015 | }; | 1107 | }; |
1016 | 1108 | ||
1017 | static ssize_t ucma_write(struct file *filp, const char __user *buf, | 1109 | static ssize_t ucma_write(struct file *filp, const char __user *buf, |