Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / xen / privcmd-buf.c
blob0f0dad427d7e6ca62f2561751ca485960a917492
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
3 /******************************************************************************
4 * privcmd-buf.c
6 * Mmap of hypercall buffers.
8 * Copyright (c) 2018 Juergen Gross
9 */
11 #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/list.h>
16 #include <linux/miscdevice.h>
17 #include <linux/mm.h>
18 #include <linux/slab.h>
20 #include "privcmd.h"
22 MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");
23 MODULE_LICENSE("GPL");
25 struct privcmd_buf_private {
26 struct mutex lock;
27 struct list_head list;
30 struct privcmd_buf_vma_private {
31 struct privcmd_buf_private *file_priv;
32 struct list_head list;
33 unsigned int users;
34 unsigned int n_pages;
35 struct page *pages[];
38 static int privcmd_buf_open(struct inode *ino, struct file *file)
40 struct privcmd_buf_private *file_priv;
42 file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
43 if (!file_priv)
44 return -ENOMEM;
46 mutex_init(&file_priv->lock);
47 INIT_LIST_HEAD(&file_priv->list);
49 file->private_data = file_priv;
51 return 0;
54 static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
56 unsigned int i;
58 list_del(&vma_priv->list);
60 for (i = 0; i < vma_priv->n_pages; i++)
61 __free_page(vma_priv->pages[i]);
63 kfree(vma_priv);
66 static int privcmd_buf_release(struct inode *ino, struct file *file)
68 struct privcmd_buf_private *file_priv = file->private_data;
69 struct privcmd_buf_vma_private *vma_priv;
71 mutex_lock(&file_priv->lock);
73 while (!list_empty(&file_priv->list)) {
74 vma_priv = list_first_entry(&file_priv->list,
75 struct privcmd_buf_vma_private,
76 list);
77 privcmd_buf_vmapriv_free(vma_priv);
80 mutex_unlock(&file_priv->lock);
82 kfree(file_priv);
84 return 0;
87 static void privcmd_buf_vma_open(struct vm_area_struct *vma)
89 struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
91 if (!vma_priv)
92 return;
94 mutex_lock(&vma_priv->file_priv->lock);
95 vma_priv->users++;
96 mutex_unlock(&vma_priv->file_priv->lock);
99 static void privcmd_buf_vma_close(struct vm_area_struct *vma)
101 struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
102 struct privcmd_buf_private *file_priv;
104 if (!vma_priv)
105 return;
107 file_priv = vma_priv->file_priv;
109 mutex_lock(&file_priv->lock);
111 vma_priv->users--;
112 if (!vma_priv->users)
113 privcmd_buf_vmapriv_free(vma_priv);
115 mutex_unlock(&file_priv->lock);
118 static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
120 pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
121 vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
122 vmf->pgoff, (void *)vmf->address);
124 return VM_FAULT_SIGBUS;
127 static const struct vm_operations_struct privcmd_buf_vm_ops = {
128 .open = privcmd_buf_vma_open,
129 .close = privcmd_buf_vma_close,
130 .fault = privcmd_buf_vma_fault,
133 static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
135 struct privcmd_buf_private *file_priv = file->private_data;
136 struct privcmd_buf_vma_private *vma_priv;
137 unsigned long count = vma_pages(vma);
138 unsigned int i;
139 int ret = 0;
141 if (!(vma->vm_flags & VM_SHARED))
142 return -EINVAL;
144 vma_priv = kzalloc(struct_size(vma_priv, pages, count), GFP_KERNEL);
145 if (!vma_priv)
146 return -ENOMEM;
148 for (i = 0; i < count; i++) {
149 vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
150 if (!vma_priv->pages[i])
151 break;
152 vma_priv->n_pages++;
155 mutex_lock(&file_priv->lock);
157 vma_priv->file_priv = file_priv;
158 vma_priv->users = 1;
160 vm_flags_set(vma, VM_IO | VM_DONTEXPAND);
161 vma->vm_ops = &privcmd_buf_vm_ops;
162 vma->vm_private_data = vma_priv;
164 list_add(&vma_priv->list, &file_priv->list);
166 if (vma_priv->n_pages != count)
167 ret = -ENOMEM;
168 else
169 ret = vm_map_pages_zero(vma, vma_priv->pages,
170 vma_priv->n_pages);
172 if (ret)
173 privcmd_buf_vmapriv_free(vma_priv);
175 mutex_unlock(&file_priv->lock);
177 return ret;
180 const struct file_operations xen_privcmdbuf_fops = {
181 .owner = THIS_MODULE,
182 .open = privcmd_buf_open,
183 .release = privcmd_buf_release,
184 .mmap = privcmd_buf_mmap,
186 EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
188 struct miscdevice xen_privcmdbuf_dev = {
189 .minor = MISC_DYNAMIC_MINOR,
190 .name = "xen/hypercall",
191 .fops = &xen_privcmdbuf_fops,