GNU Linux-libre 4.19.207-gnu1
[releases.git] / drivers / xen / privcmd-buf.c
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2
3 /******************************************************************************
4  * privcmd-buf.c
5  *
6  * Mmap of hypercall buffers.
7  *
8  * Copyright (c) 2018 Juergen Gross
9  */
10
11 #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
12
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>
19
20 #include "privcmd.h"
21
22 MODULE_LICENSE("GPL");
23
24 struct privcmd_buf_private {
25         struct mutex lock;
26         struct list_head list;
27 };
28
29 struct privcmd_buf_vma_private {
30         struct privcmd_buf_private *file_priv;
31         struct list_head list;
32         unsigned int users;
33         unsigned int n_pages;
34         struct page *pages[];
35 };
36
37 static int privcmd_buf_open(struct inode *ino, struct file *file)
38 {
39         struct privcmd_buf_private *file_priv;
40
41         file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
42         if (!file_priv)
43                 return -ENOMEM;
44
45         mutex_init(&file_priv->lock);
46         INIT_LIST_HEAD(&file_priv->list);
47
48         file->private_data = file_priv;
49
50         return 0;
51 }
52
53 static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
54 {
55         unsigned int i;
56
57         list_del(&vma_priv->list);
58
59         for (i = 0; i < vma_priv->n_pages; i++)
60                 __free_page(vma_priv->pages[i]);
61
62         kfree(vma_priv);
63 }
64
65 static int privcmd_buf_release(struct inode *ino, struct file *file)
66 {
67         struct privcmd_buf_private *file_priv = file->private_data;
68         struct privcmd_buf_vma_private *vma_priv;
69
70         mutex_lock(&file_priv->lock);
71
72         while (!list_empty(&file_priv->list)) {
73                 vma_priv = list_first_entry(&file_priv->list,
74                                             struct privcmd_buf_vma_private,
75                                             list);
76                 privcmd_buf_vmapriv_free(vma_priv);
77         }
78
79         mutex_unlock(&file_priv->lock);
80
81         kfree(file_priv);
82
83         return 0;
84 }
85
86 static void privcmd_buf_vma_open(struct vm_area_struct *vma)
87 {
88         struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
89
90         if (!vma_priv)
91                 return;
92
93         mutex_lock(&vma_priv->file_priv->lock);
94         vma_priv->users++;
95         mutex_unlock(&vma_priv->file_priv->lock);
96 }
97
98 static void privcmd_buf_vma_close(struct vm_area_struct *vma)
99 {
100         struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
101         struct privcmd_buf_private *file_priv;
102
103         if (!vma_priv)
104                 return;
105
106         file_priv = vma_priv->file_priv;
107
108         mutex_lock(&file_priv->lock);
109
110         vma_priv->users--;
111         if (!vma_priv->users)
112                 privcmd_buf_vmapriv_free(vma_priv);
113
114         mutex_unlock(&file_priv->lock);
115 }
116
117 static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
118 {
119         pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
120                  vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
121                  vmf->pgoff, (void *)vmf->address);
122
123         return VM_FAULT_SIGBUS;
124 }
125
126 static const struct vm_operations_struct privcmd_buf_vm_ops = {
127         .open = privcmd_buf_vma_open,
128         .close = privcmd_buf_vma_close,
129         .fault = privcmd_buf_vma_fault,
130 };
131
132 static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
133 {
134         struct privcmd_buf_private *file_priv = file->private_data;
135         struct privcmd_buf_vma_private *vma_priv;
136         unsigned long count = vma_pages(vma);
137         unsigned int i;
138         int ret = 0;
139
140         if (!(vma->vm_flags & VM_SHARED))
141                 return -EINVAL;
142
143         vma_priv = kzalloc(sizeof(*vma_priv) + count * sizeof(void *),
144                            GFP_KERNEL);
145         if (!vma_priv)
146                 return -ENOMEM;
147
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++;
153         }
154
155         mutex_lock(&file_priv->lock);
156
157         vma_priv->file_priv = file_priv;
158         vma_priv->users = 1;
159
160         vma->vm_flags |= VM_IO | VM_DONTEXPAND;
161         vma->vm_ops = &privcmd_buf_vm_ops;
162         vma->vm_private_data = vma_priv;
163
164         list_add(&vma_priv->list, &file_priv->list);
165
166         if (vma_priv->n_pages != count)
167                 ret = -ENOMEM;
168         else
169                 for (i = 0; i < vma_priv->n_pages; i++) {
170                         ret = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
171                                              vma_priv->pages[i]);
172                         if (ret)
173                                 break;
174                 }
175
176         if (ret)
177                 privcmd_buf_vmapriv_free(vma_priv);
178
179         mutex_unlock(&file_priv->lock);
180
181         return ret;
182 }
183
184 const struct file_operations xen_privcmdbuf_fops = {
185         .owner = THIS_MODULE,
186         .open = privcmd_buf_open,
187         .release = privcmd_buf_release,
188         .mmap = privcmd_buf_mmap,
189 };
190 EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
191
192 struct miscdevice xen_privcmdbuf_dev = {
193         .minor = MISC_DYNAMIC_MINOR,
194         .name = "xen/hypercall",
195         .fops = &xen_privcmdbuf_fops,
196 };