Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 22MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");
 23MODULE_LICENSE("GPL");
 24
 25struct privcmd_buf_private {
 26	struct mutex lock;
 27	struct list_head list;
 28};
 29
 30struct 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
 38static 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
 54static 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
 66static 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
 87static 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
 99static 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
118static 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
127static 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
133static 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
180const 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};
186EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
187
188struct miscdevice xen_privcmdbuf_dev = {
189	.minor = MISC_DYNAMIC_MINOR,
190	.name = "xen/hypercall",
191	.fops = &xen_privcmdbuf_fops,
192};