/* * Copyright (c) 2005-2006 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.
*/
xa_lock(&ctx_table);
ctx = _ucma_find_context(id, file); if (!IS_ERR(ctx)) if (!refcount_inc_not_zero(&ctx->ref))
ctx = ERR_PTR(-ENXIO);
xa_unlock(&ctx_table); return ctx;
}
staticvoid ucma_put_ctx(struct ucma_context *ctx)
{ if (refcount_dec_and_test(&ctx->ref))
complete(&ctx->comp);
}
/* * Same as ucm_get_ctx but requires that ->cm_id->device is valid, eg that the * CM_ID is bound.
*/ staticstruct ucma_context *ucma_get_ctx_dev(struct ucma_file *file, int id)
{ struct ucma_context *ctx = ucma_get_ctx(file, id);
if (IS_ERR(ctx)) return ctx; if (!ctx->cm_id->device) {
ucma_put_ctx(ctx); return ERR_PTR(-EINVAL);
} return ctx;
}
/* once all inflight tasks are finished, we close all underlying * resources. The context is still alive till its explicit destryoing * by its creator. This puts back the xarray's reference.
*/
ucma_put_ctx(ctx);
wait_for_completion(&ctx->comp); /* No new events will be generated after destroying the id. */
rdma_destroy_id(ctx->cm_id);
/* Reading the cm_id without holding a positive ref is not allowed */
ctx->cm_id = NULL;
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL;
INIT_WORK(&ctx->close_work, ucma_close_id);
init_completion(&ctx->comp);
INIT_LIST_HEAD(&ctx->mc_list); /* So list_del() will work if we don't do ucma_finish_ctx() */
INIT_LIST_HEAD(&ctx->list);
ctx->file = file;
mutex_init(&ctx->mutex);
err_alloc:
ucma_destroy_private_ctx(ctx);
err_backlog:
atomic_inc(&listen_ctx->backlog); /* Returning error causes the new ID to be destroyed */ return -ENOMEM;
}
if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) return ucma_connect_event_handler(cm_id, event);
/* * We ignore events for new connections until userspace has set their * context. This can only happen if an error occurs on a new connection * before the user accepts it. This is okay, since the accept will just * fail later. However, we do need to release the underlying HW * resources in case of a device removal event.
*/ if (ctx->uid) {
uevent = ucma_create_uevent(ctx, event); if (!uevent) return 0;
if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
xa_lock(&ctx_table); if (xa_load(&ctx_table, ctx->id) == ctx)
queue_work(system_unbound_wq, &ctx->close_work);
xa_unlock(&ctx_table);
} return 0;
}
static ssize_t ucma_get_event(struct ucma_file *file, constchar __user *inbuf, int in_len, int out_len)
{ struct rdma_ucm_get_event cmd; struct ucma_event *uevent;
/* * Old 32 bit user space does not send the 4 byte padding in the * reserved field. We don't care, allow it to keep working.
*/ if (out_len < sizeof(uevent->resp) - sizeof(uevent->resp.reserved) - sizeof(uevent->resp.ece)) return -ENOSPC;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT;
mutex_lock(&file->mut); while (list_empty(&file->event_list)) {
mutex_unlock(&file->mut);
if (file->filp->f_flags & O_NONBLOCK) return -EAGAIN;
if (wait_event_interruptible(file->poll_wait,
!list_empty(&file->event_list))) return -ERESTARTSYS;
xa_lock(&multicast_table);
list_for_each_entry_safe(mc, tmp, &ctx->mc_list, list) {
list_del(&mc->list); /* * At this point mc->ctx->ref is 0 so the mc cannot leave the * lock on the reader and this is enough serialization
*/
__xa_erase(&multicast_table, mc->id);
kfree(mc);
}
xa_unlock(&multicast_table);
}
/* Cleanup events not yet reported to the user.*/
mutex_lock(&ctx->file->mut);
list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) { if (uevent->ctx != ctx) continue;
/* * If this was a listening ID then any connections spawned from it that * have not been delivered to userspace are cleaned up too. Must be done * outside any locks.
*/
list_for_each_entry_safe(uevent, tmp, &list, list) {
ucma_destroy_private_ctx(uevent->conn_req_ctx);
kfree(uevent);
} return events_reported;
}
/* * When this is called the xarray must have a XA_ZERO_ENTRY in the ctx->id (ie * the ctx is not public to the user). This either because: * - ucma_finish_ctx() hasn't been called * - xa_cmpxchg() succeed to remove the entry (only one thread can succeed)
*/ staticint ucma_destroy_private_ctx(struct ucma_context *ctx)
{ int events_reported;
/* * Destroy the underlying cm_id. New work queuing is prevented now by * the removal from the xarray. Once the work is cancled ref will either * be 0 because the work ran to completion and consumed the ref from the * xarray, or it will be positive because we still have the ref from the * xarray. This can also be 0 in cases where cm_id was never set
*/
cancel_work_sync(&ctx->close_work); if (refcount_read(&ctx->ref))
ucma_close_id(&ctx->close_work);
mutex_lock(&ctx->mutex); switch (cmd.option) { case RDMA_USER_CM_QUERY_ADDR:
ret = ucma_query_addr(ctx, response, out_len); break; case RDMA_USER_CM_QUERY_PATH:
ret = ucma_query_path(ctx, response, out_len); break; case RDMA_USER_CM_QUERY_GID:
ret = ucma_query_gid(ctx, response, out_len); break; default:
ret = -ENOSYS; break;
}
mutex_unlock(&ctx->mutex);
if (cmd.conn_param.valid) {
ucma_copy_conn_param(ctx->cm_id, &conn_param, &cmd.conn_param);
mutex_lock(&ctx->mutex);
rdma_lock_handler(ctx->cm_id);
ret = rdma_accept_ece(ctx->cm_id, &conn_param, &ece); if (!ret) { /* The uid must be set atomically with the handler */
ctx->uid = cmd.uid;
}
rdma_unlock_handler(ctx->cm_id);
mutex_unlock(&ctx->mutex);
} else {
mutex_lock(&ctx->mutex);
rdma_lock_handler(ctx->cm_id);
ret = rdma_accept_ece(ctx->cm_id, NULL, &ece);
rdma_unlock_handler(ctx->cm_id);
mutex_unlock(&ctx->mutex);
}
ucma_put_ctx(ctx); return ret;
}
static ssize_t ucma_reject(struct ucma_file *file, constchar __user *inbuf, int in_len, int out_len)
{ struct rdma_ucm_reject cmd; struct ucma_context *ctx; int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT;
if (!cmd.reason)
cmd.reason = IB_CM_REJ_CONSUMER_DEFINED;
switch (cmd.reason) { case IB_CM_REJ_CONSUMER_DEFINED: case IB_CM_REJ_VENDOR_OPTION_NOT_SUPPORTED: break; default: return -EINVAL;
}
ctx = ucma_get_ctx_dev(file, cmd.id); if (IS_ERR(ctx)) return PTR_ERR(ctx);
static ssize_t ucma_join_multicast(struct ucma_file *file, constchar __user *inbuf, int in_len, int out_len)
{ struct rdma_ucm_join_mcast cmd;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT;
if (!rdma_addr_size_kss(&cmd.addr)) return -EINVAL;
return ucma_process_join(file, &cmd, out_len);
}
static ssize_t ucma_leave_multicast(struct ucma_file *file, constchar __user *inbuf, int in_len, int out_len)
{ struct rdma_ucm_destroy_id cmd; struct rdma_ucm_destroy_id_resp resp; struct ucma_multicast *mc; int ret = 0;
if (out_len < sizeof(resp)) return -ENOSPC;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT;
xa_lock(&multicast_table);
mc = xa_load(&multicast_table, cmd.id); if (!mc)
mc = ERR_PTR(-ENOENT); elseif (READ_ONCE(mc->ctx->file) != file)
mc = ERR_PTR(-EINVAL); elseif (!refcount_inc_not_zero(&mc->ctx->ref))
mc = ERR_PTR(-ENXIO);
if (IS_ERR(mc)) {
xa_unlock(&multicast_table);
ret = PTR_ERR(mc); goto out;
}
if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
ret = -EFAULT;
out: return ret;
}
static ssize_t ucma_migrate_id(struct ucma_file *new_file, constchar __user *inbuf, int in_len, int out_len)
{ struct rdma_ucm_migrate_id cmd; struct rdma_ucm_migrate_resp resp; struct ucma_event *uevent, *tmp; struct ucma_context *ctx;
LIST_HEAD(event_list); struct ucma_file *cur_file; int ret = 0;
if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT;
/* Get current fd to protect against it being closed */ CLASS(fd, f)(cmd.fd); if (fd_empty(f)) return -ENOENT; if (fd_file(f)->f_op != &ucma_fops) return -EINVAL;
cur_file = fd_file(f)->private_data;
/* Validate current fd and prevent destruction of id. */
ctx = ucma_get_ctx(cur_file, cmd.id); if (IS_ERR(ctx)) return PTR_ERR(ctx);
rdma_lock_handler(ctx->cm_id); /* * ctx->file can only be changed under the handler & xa_lock. xa_load() * must be checked again to ensure the ctx hasn't begun destruction * since the ucma_get_ctx().
*/
xa_lock(&ctx_table); if (_ucma_find_context(cmd.id, cur_file) != ctx) {
xa_unlock(&ctx_table);
ret = -ENOENT; goto err_unlock;
}
ctx->file = new_file;
xa_unlock(&ctx_table);
mutex_lock(&cur_file->mut);
list_del(&ctx->list); /* * At this point lock_handler() prevents addition of new uevents for * this ctx.
*/
list_for_each_entry_safe(uevent, tmp, &cur_file->event_list, list) if (uevent->ctx == ctx)
list_move_tail(&uevent->list, &event_list);
resp.events_reported = ctx->events_reported;
mutex_unlock(&cur_file->mut);
if (!ib_safe_file_access(filp)) {
pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n",
__func__, task_tgid_vnr(current), current->comm); return -EACCES;
}
if (len < sizeof(hdr)) return -EINVAL;
if (copy_from_user(&hdr, buf, sizeof(hdr))) return -EFAULT;
if (hdr.cmd >= ARRAY_SIZE(ucma_cmd_table)) return -EINVAL;
hdr.cmd = array_index_nospec(hdr.cmd, ARRAY_SIZE(ucma_cmd_table));
if (hdr.in + sizeof(hdr) > len) return -EINVAL;
if (!ucma_cmd_table[hdr.cmd]) return -ENOSYS;
ret = ucma_cmd_table[hdr.cmd](file, buf + sizeof(hdr), hdr.in, hdr.out); if (!ret)
ret = len;
if (!list_empty(&file->event_list))
mask = EPOLLIN | EPOLLRDNORM;
return mask;
}
/* * ucma_open() does not need the BKL: * * - no global state is referred to; * - there is no ioctl method to race against; * - no further module initialization is required for open to work * after the device is registered.
*/ staticint ucma_open(struct inode *inode, struct file *filp)
{ struct ucma_file *file;
file = kmalloc(sizeof *file, GFP_KERNEL); if (!file) return -ENOMEM;
/* * All paths that touch ctx_list or ctx_list starting from write() are * prevented by this being a FD release function. The list_add_tail() in * ucma_connect_event_handler() can run concurrently, however it only * adds to the list *after* a listening ID. By only reading the first of * the list, and relying on ucma_destroy_private_ctx() to block * ucma_connect_event_handler(), no additional locking is needed.
*/ while (!list_empty(&file->ctx_list)) { struct ucma_context *ctx = list_first_entry(
&file->ctx_list, struct ucma_context, list);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.