aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/core/uverbs_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/infiniband/core/uverbs_main.c')
-rw-r--r--drivers/infiniband/core/uverbs_main.c121
1 files changed, 114 insertions, 7 deletions
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 259dcc7779f5e..1aae7ed09d2f8 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -116,6 +116,9 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file,
[IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd,
[IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq,
[IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp,
+ [IB_USER_VERBS_CMD_CREATE_MMU_NOTIFY_CHANNEL] = ib_uverbs_create_mmu_notify_channel,
+ [IB_USER_VERBS_CMD_REG_MMU_NOTIFY_MR] = ib_uverbs_reg_mmu_notify_mr,
+ [IB_USER_VERBS_CMD_DEREG_MMU_NOTIFY_MR] = ib_uverbs_dereg_mmu_notify_mr,
};
static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
@@ -271,9 +274,15 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->mr_list, list) {
struct ib_mr *mr = uobj->object;
+ struct ib_umr_object *umr =
+ container_of(uobj, struct ib_umr_object, uevent.uobject);
idr_remove_uobj(&ib_uverbs_mr_idr, uobj);
+ if (ib_ummunotify_context_used(&file->mmu_notify_context))
+ ib_ummunotify_unregister_range(&file->mmu_notify_context,
+ &umr->range);
ib_dereg_mr(mr);
+ ib_uverbs_release_uevent(file, &umr->uevent);
kfree(uobj);
}
@@ -298,6 +307,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
}
put_pid(context->tgid);
+ ib_ummunotify_cleanup_context(&file->mmu_notify_context);
+ kfree(file->mmu_notify_counter);
return context->device->dealloc_ucontext(context);
}
@@ -318,7 +329,7 @@ static ssize_t ib_uverbs_event_read(struct file *filp, char __user *buf,
{
struct ib_uverbs_event_file *file = filp->private_data;
struct ib_uverbs_event *event;
- int eventsz;
+ int uninitialized_var(eventsz);
int ret = 0;
spin_lock_irq(&file->lock);
@@ -338,10 +349,17 @@ static ssize_t ib_uverbs_event_read(struct file *filp, char __user *buf,
event = list_entry(file->event_list.next, struct ib_uverbs_event, list);
- if (file->is_async)
+ switch (file->type) {
+ case IB_UVERBS_EVENT_FILE_ASYNC:
eventsz = sizeof (struct ib_uverbs_async_event_desc);
- else
+ break;
+ case IB_UVERBS_EVENT_FILE_COMP:
eventsz = sizeof (struct ib_uverbs_comp_event_desc);
+ break;
+ case IB_UVERBS_EVENT_FILE_MMU_NOTIFY:
+ eventsz = sizeof (struct ib_uverbs_mmu_notify_event_desc);
+ break;
+ }
if (eventsz > count) {
ret = -EINVAL;
@@ -368,6 +386,37 @@ static ssize_t ib_uverbs_event_read(struct file *filp, char __user *buf,
return ret;
}
+static int uverbs_mmu_notify_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct ib_uverbs_file *file = vma->vm_private_data;
+
+ if (vmf->pgoff != 0)
+ return VM_FAULT_SIGBUS;
+
+ vmf->page = virt_to_page(file->mmu_notify_counter);
+ get_page(vmf->page);
+
+ return 0;
+}
+
+static const struct vm_operations_struct uverbs_mmu_notify_vm_ops = {
+ .fault = uverbs_mmu_notify_fault,
+};
+
+static int ib_uverbs_event_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct ib_uverbs_event_file *ev_file = filp->private_data;
+ struct ib_uverbs_file *file = ev_file->uverbs_file;
+
+ if (vma->vm_end - vma->vm_start != PAGE_SIZE || vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ vma->vm_ops = &uverbs_mmu_notify_vm_ops;
+ vma->vm_private_data = file;
+
+ return 0;
+}
+
static unsigned int ib_uverbs_event_poll(struct file *filp,
struct poll_table_struct *wait)
{
@@ -405,10 +454,15 @@ static int ib_uverbs_event_close(struct inode *inode, struct file *filp)
}
spin_unlock_irq(&file->lock);
- if (file->is_async) {
+ if (file->type == IB_UVERBS_EVENT_FILE_ASYNC) {
ib_unregister_event_handler(&file->uverbs_file->event_handler);
kref_put(&file->uverbs_file->ref, ib_uverbs_release_file);
}
+
+ if (file->type == IB_UVERBS_EVENT_FILE_MMU_NOTIFY) {
+ /* XXX */
+ }
+
kref_put(&file->ref, ib_uverbs_release_event_file);
return 0;
@@ -423,6 +477,16 @@ static const struct file_operations uverbs_event_fops = {
.llseek = no_llseek,
};
+static const struct file_operations uverbs_event_mmap_fops = {
+ .owner = THIS_MODULE,
+ .read = ib_uverbs_event_read,
+ .mmap = ib_uverbs_event_mmap,
+ .poll = ib_uverbs_event_poll,
+ .release = ib_uverbs_event_close,
+ .fasync = ib_uverbs_event_fasync,
+ .llseek = no_llseek,
+};
+
void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context)
{
struct ib_uverbs_event_file *file = cq_context;
@@ -458,6 +522,47 @@ void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context)
kill_fasync(&file->async_queue, SIGIO, POLL_IN);
}
+void ib_uverbs_mr_event_handler(struct ib_ummunotify_context *context,
+ struct ib_ummunotify_range *range)
+{
+ struct ib_uverbs_event_file *file =
+ container_of(context, struct ib_uverbs_file,
+ mmu_notify_context)->mmu_notify_file;
+ struct ib_umr_object *uobj;
+ struct ib_uverbs_event *entry;
+ unsigned long flags;
+
+ if (!file)
+ return;
+
+ spin_lock_irqsave(&file->lock, flags);
+ if (file->is_closed) {
+ spin_unlock_irqrestore(&file->lock, flags);
+ return;
+ }
+
+ entry = kmalloc(sizeof *entry, GFP_ATOMIC);
+ if (!entry) {
+ spin_unlock_irqrestore(&file->lock, flags);
+ return;
+ }
+
+ uobj = container_of(range, struct ib_umr_object, range);
+
+ entry->desc.mmu_notify.cq_handle = uobj->uevent.uobject.user_handle;
+ entry->counter = &uobj->uevent.events_reported;
+
+ list_add_tail(&entry->list, &file->event_list);
+ list_add_tail(&entry->obj_list, &uobj->uevent.event_list);
+
+ ++(*file->uverbs_file->mmu_notify_counter);
+
+ spin_unlock_irqrestore(&file->lock, flags);
+
+ wake_up_interruptible(&file->poll_wait);
+ kill_fasync(&file->async_queue, SIGIO, POLL_IN);
+}
+
static void ib_uverbs_async_handler(struct ib_uverbs_file *file,
__u64 element, __u64 event,
struct list_head *obj_list,
@@ -541,7 +646,7 @@ void ib_uverbs_event_handler(struct ib_event_handler *handler,
}
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
- int is_async)
+ enum ib_uverbs_event_file_type type)
{
struct ib_uverbs_event_file *ev_file;
struct file *filp;
@@ -556,7 +661,7 @@ struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
init_waitqueue_head(&ev_file->poll_wait);
ev_file->uverbs_file = uverbs_file;
ev_file->async_queue = NULL;
- ev_file->is_async = is_async;
+ ev_file->type = type;
ev_file->is_closed = 0;
filp = anon_inode_getfile("[infinibandevent]", &uverbs_event_fops,
@@ -584,7 +689,7 @@ struct ib_uverbs_event_file *ib_uverbs_lookup_comp_file(int fd)
goto out;
ev_file = f.file->private_data;
- if (ev_file->is_async) {
+ if (ev_file->type != IB_UVERBS_EVENT_FILE_COMP) {
ev_file = NULL;
goto out;
}
@@ -763,6 +868,8 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
file->async_file = NULL;
kref_init(&file->ref);
mutex_init(&file->mutex);
+ ib_ummunotify_clear_context(&file->mmu_notify_context);
+ file->mmu_notify_counter = NULL;
filp->private_data = file;