]> Git Repo - J-linux.git/blob - drivers/xen/privcmd-buf.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.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_DESCRIPTION("Xen Mmap of hypercall buffers");
23 MODULE_LICENSE("GPL");
24
25 struct privcmd_buf_private {
26         struct mutex lock;
27         struct list_head list;
28 };
29
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[];
36 };
37
38 static int privcmd_buf_open(struct inode *ino, struct file *file)
39 {
40         struct privcmd_buf_private *file_priv;
41
42         file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
43         if (!file_priv)
44                 return -ENOMEM;
45
46         mutex_init(&file_priv->lock);
47         INIT_LIST_HEAD(&file_priv->list);
48
49         file->private_data = file_priv;
50
51         return 0;
52 }
53
54 static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
55 {
56         unsigned int i;
57
58         list_del(&vma_priv->list);
59
60         for (i = 0; i < vma_priv->n_pages; i++)
61                 __free_page(vma_priv->pages[i]);
62
63         kfree(vma_priv);
64 }
65
66 static int privcmd_buf_release(struct inode *ino, struct file *file)
67 {
68         struct privcmd_buf_private *file_priv = file->private_data;
69         struct privcmd_buf_vma_private *vma_priv;
70
71         mutex_lock(&file_priv->lock);
72
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);
78         }
79
80         mutex_unlock(&file_priv->lock);
81
82         kfree(file_priv);
83
84         return 0;
85 }
86
87 static void privcmd_buf_vma_open(struct vm_area_struct *vma)
88 {
89         struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
90
91         if (!vma_priv)
92                 return;
93
94         mutex_lock(&vma_priv->file_priv->lock);
95         vma_priv->users++;
96         mutex_unlock(&vma_priv->file_priv->lock);
97 }
98
99 static void privcmd_buf_vma_close(struct vm_area_struct *vma)
100 {
101         struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
102         struct privcmd_buf_private *file_priv;
103
104         if (!vma_priv)
105                 return;
106
107         file_priv = vma_priv->file_priv;
108
109         mutex_lock(&file_priv->lock);
110
111         vma_priv->users--;
112         if (!vma_priv->users)
113                 privcmd_buf_vmapriv_free(vma_priv);
114
115         mutex_unlock(&file_priv->lock);
116 }
117
118 static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
119 {
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);
123
124         return VM_FAULT_SIGBUS;
125 }
126
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,
131 };
132
133 static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
134 {
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;
140
141         if (!(vma->vm_flags & VM_SHARED))
142                 return -EINVAL;
143
144         vma_priv = kzalloc(struct_size(vma_priv, pages, count), 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         vm_flags_set(vma, 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                 ret = vm_map_pages_zero(vma, vma_priv->pages,
170                                                 vma_priv->n_pages);
171
172         if (ret)
173                 privcmd_buf_vmapriv_free(vma_priv);
174
175         mutex_unlock(&file_priv->lock);
176
177         return ret;
178 }
179
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,
185 };
186 EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
187
188 struct miscdevice xen_privcmdbuf_dev = {
189         .minor = MISC_DYNAMIC_MINOR,
190         .name = "xen/hypercall",
191         .fops = &xen_privcmdbuf_fops,
192 };
This page took 0.055054 seconds and 4 git commands to generate.