1 // SPDX-License-Identifier: GPL-2.0 OR MIT
3 /******************************************************************************
6 * Mmap of hypercall buffers.
8 * Copyright (c) 2018 Juergen Gross
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>
18 #include <linux/slab.h>
22 MODULE_LICENSE("GPL");
24 static unsigned int limit
= 64;
25 module_param(limit
, uint
, 0644);
26 MODULE_PARM_DESC(limit
, "Maximum number of pages that may be allocated by "
27 "the privcmd-buf device per open file");
29 struct privcmd_buf_private
{
31 struct list_head list
;
32 unsigned int allocated
;
35 struct privcmd_buf_vma_private
{
36 struct privcmd_buf_private
*file_priv
;
37 struct list_head list
;
43 static int privcmd_buf_open(struct inode
*ino
, struct file
*file
)
45 struct privcmd_buf_private
*file_priv
;
47 file_priv
= kzalloc(sizeof(*file_priv
), GFP_KERNEL
);
51 mutex_init(&file_priv
->lock
);
52 INIT_LIST_HEAD(&file_priv
->list
);
54 file
->private_data
= file_priv
;
59 static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private
*vma_priv
)
63 vma_priv
->file_priv
->allocated
-= vma_priv
->n_pages
;
65 list_del(&vma_priv
->list
);
67 for (i
= 0; i
< vma_priv
->n_pages
; i
++)
68 if (vma_priv
->pages
[i
])
69 __free_page(vma_priv
->pages
[i
]);
74 static int privcmd_buf_release(struct inode
*ino
, struct file
*file
)
76 struct privcmd_buf_private
*file_priv
= file
->private_data
;
77 struct privcmd_buf_vma_private
*vma_priv
;
79 mutex_lock(&file_priv
->lock
);
81 while (!list_empty(&file_priv
->list
)) {
82 vma_priv
= list_first_entry(&file_priv
->list
,
83 struct privcmd_buf_vma_private
,
85 privcmd_buf_vmapriv_free(vma_priv
);
88 mutex_unlock(&file_priv
->lock
);
95 static void privcmd_buf_vma_open(struct vm_area_struct
*vma
)
97 struct privcmd_buf_vma_private
*vma_priv
= vma
->vm_private_data
;
102 mutex_lock(&vma_priv
->file_priv
->lock
);
104 mutex_unlock(&vma_priv
->file_priv
->lock
);
107 static void privcmd_buf_vma_close(struct vm_area_struct
*vma
)
109 struct privcmd_buf_vma_private
*vma_priv
= vma
->vm_private_data
;
110 struct privcmd_buf_private
*file_priv
;
115 file_priv
= vma_priv
->file_priv
;
117 mutex_lock(&file_priv
->lock
);
120 if (!vma_priv
->users
)
121 privcmd_buf_vmapriv_free(vma_priv
);
123 mutex_unlock(&file_priv
->lock
);
126 static vm_fault_t
privcmd_buf_vma_fault(struct vm_fault
*vmf
)
128 pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
129 vmf
->vma
, vmf
->vma
->vm_start
, vmf
->vma
->vm_end
,
130 vmf
->pgoff
, (void *)vmf
->address
);
132 return VM_FAULT_SIGBUS
;
135 static const struct vm_operations_struct privcmd_buf_vm_ops
= {
136 .open
= privcmd_buf_vma_open
,
137 .close
= privcmd_buf_vma_close
,
138 .fault
= privcmd_buf_vma_fault
,
141 static int privcmd_buf_mmap(struct file
*file
, struct vm_area_struct
*vma
)
143 struct privcmd_buf_private
*file_priv
= file
->private_data
;
144 struct privcmd_buf_vma_private
*vma_priv
;
145 unsigned long count
= vma_pages(vma
);
149 if (!(vma
->vm_flags
& VM_SHARED
) || count
> limit
||
150 file_priv
->allocated
+ count
> limit
)
153 vma_priv
= kzalloc(sizeof(*vma_priv
) + count
* sizeof(void *),
158 vma_priv
->n_pages
= count
;
160 for (i
= 0; i
< vma_priv
->n_pages
; i
++) {
161 vma_priv
->pages
[i
] = alloc_page(GFP_KERNEL
| __GFP_ZERO
);
162 if (!vma_priv
->pages
[i
])
167 mutex_lock(&file_priv
->lock
);
169 file_priv
->allocated
+= count
;
171 vma_priv
->file_priv
= file_priv
;
174 vma
->vm_flags
|= VM_IO
| VM_DONTEXPAND
;
175 vma
->vm_ops
= &privcmd_buf_vm_ops
;
176 vma
->vm_private_data
= vma_priv
;
178 list_add(&vma_priv
->list
, &file_priv
->list
);
180 if (vma_priv
->n_pages
!= count
)
183 for (i
= 0; i
< vma_priv
->n_pages
; i
++) {
184 ret
= vm_insert_page(vma
, vma
->vm_start
+ i
* PAGE_SIZE
,
191 privcmd_buf_vmapriv_free(vma_priv
);
193 mutex_unlock(&file_priv
->lock
);
198 const struct file_operations xen_privcmdbuf_fops
= {
199 .owner
= THIS_MODULE
,
200 .open
= privcmd_buf_open
,
201 .release
= privcmd_buf_release
,
202 .mmap
= privcmd_buf_mmap
,
204 EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops
);
206 struct miscdevice xen_privcmdbuf_dev
= {
207 .minor
= MISC_DYNAMIC_MINOR
,
208 .name
= "xen/hypercall",
209 .fops
= &xen_privcmdbuf_fops
,